Skip to content

Commit

Permalink
Fix some bugs (#404)
Browse files Browse the repository at this point in the history
* Readd RemoveSeparatedSectionMutator, but removed it from the list of default NpmMutators.

* Fix issues where mutations would occur of just separators. ex: pkg:npm/q would create pkg:npm/. as a squat.
Add test for it.

* Fix bug where scoped npm packages would have the prefix added before the @. Added test case for it.

* Fix issue where mutations that were empty could be generated. Added tests to validate it.
  • Loading branch information
jpinz authored Mar 6, 2023
1 parent 84d659e commit 730b367
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/oss-find-squats-lib/MutateExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public static class MutateExtension
new PrefixMutator(),
new RemovedCharacterMutator(),
new RemoveNamespaceMutator(),
new RemoveSeparatedSectionMutator(),
new ReplaceCharacterMutator(),
new SeparatorChangedMutator(),
new SeparatorRemovedMutator(),
Expand All @@ -58,7 +59,7 @@ public static class MutateExtension
/// <summary>
/// Common variations known uniquely for NPM/Javascript.
/// </summary>
internal static IEnumerable<IMutator> NpmMutators { get; } = BaseMutators.Where(x => x is not UnicodeHomoglyphMutator and not PrefixMutator and not SuffixMutator)
internal static IEnumerable<IMutator> NpmMutators { get; } = BaseMutators.Where(x => x is not UnicodeHomoglyphMutator and not PrefixMutator and not SuffixMutator and not RemoveSeparatedSectionMutator)
.Concat(new IMutator[]
{
new SubstitutionMutator(new List<(string Original, string Substitution)>()
Expand Down
13 changes: 13 additions & 0 deletions src/oss-find-squats-lib/Mutators/IMutator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ namespace Microsoft.CST.OpenSource.FindSquats.Mutators
{
using Extensions;
using PackageUrl;
using System;
using System.Collections.Generic;
using System.Web;

Expand Down Expand Up @@ -35,6 +36,18 @@ public IEnumerable<Mutation> Generate(PackageURL arg)
bool hasNamespace = arg.HasNamespace();
foreach (Mutation mutation in Generate(hasNamespace ? arg.Namespace : arg.Name))
{
if (mutation.Mutated.Length == 0)
{
// Don't make mutations that are empty. i.e pkg:npm/ isn't valid.
continue;
}

if (mutation.Mutated.Length == 1 && !char.IsLetterOrDigit(mutation.Mutated[0]))
{
// Don't make mutations that are just one separator. i.e pkg:npm/. isn't valid.
continue;
}

if (hasNamespace)
{
yield return new Mutation(
Expand Down
18 changes: 18 additions & 0 deletions src/oss-find-squats-lib/Mutators/PrefixMutator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,24 @@ public PrefixMutator(string[]? additionalPrefixes = null, string[]? overridePref

public IEnumerable<Mutation> Generate(string arg)
{
// Can't add a character before an @ for a scoped package.
if (arg.StartsWith('@'))
{
var addedPrefixesWithScope = _prefixes.Select(s => new Mutation(
mutated: $"@{s}{arg[1..]}",
original: arg,
mutator: Kind,
reason: $"Prefix Added: {s}"));

var removedPrefixesWithScope = _prefixes.Where(arg[1..].StartsWith).Select(s => new Mutation(
mutated: '@' + arg[1..].ReplaceAtStart(s, string.Empty),
original: arg,
mutator: Kind,
reason: $"Prefix Removed: {s}"));

return addedPrefixesWithScope.Concat(removedPrefixesWithScope);
}

var addedPrefixes = _prefixes.Select(s => new Mutation(
mutated: string.Concat(s, arg),
original: arg,
Expand Down
67 changes: 65 additions & 2 deletions src/oss-tests/FindSquatsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ public async Task DetectSquats(string packageUrl, bool generateSquats, bool expe
}

[DataTestMethod]
[DataRow("pkg:npm/angular/core", "pkg:npm/engular/core", "pkg:npm/angullar/core", "pkg:npm/core", "pkg:npm/angular-core", "pkg:npm/angular.core", "pkg:npm/angularcore")]
[DataRow("pkg:npm/%40angular/core", "pkg:npm/%40engular/core", "pkg:npm/%40angullar/core", "pkg:npm/core","pkg:npm/angular-core", "pkg:npm/angular.core", "pkg:npm/angularcore")] // back compat check
[DataRow("pkg:npm/angular/core", "pkg:npm/engular/core", "pkg:npm/angullar/core", "pkg:npm/node-angular/core", "pkg:npm/core", "pkg:npm/angular-core", "pkg:npm/angular.core", "pkg:npm/angularcore")]
[DataRow("pkg:npm/%40angular/core", "pkg:npm/%40engular/core", "pkg:npm/%40angullar/core", "pkg:npm/%40node-angular/core", "pkg:npm/core","pkg:npm/angular-core", "pkg:npm/angular.core", "pkg:npm/angularcore")] // back compat check
[DataRow("pkg:npm/lodash", "pkg:npm/odash", "pkg:npm/lodah")]
[DataRow("pkg:npm/babel/runtime", "pkg:npm/abel/runtime", "pkg:npm/bable/runtime", "pkg:npm/runtime")]
public void ScopedNpmPackageSquats(string packageUrl, params string[] expectedSquats)
Expand Down Expand Up @@ -144,6 +144,69 @@ public void ExcludeRemoveNamespaceMutator(string packageUrl)
Assert.Fail($"Found a mutation with a removed namespace, which shouldn't happen");
}

[DataTestMethod]
[DataRow("pkg:npm/i")]
[DataRow("pkg:npm/ts")]
[DataRow("pkg:nuget/d")]
[DataRow("pkg:pypi/python")]
public void DontMakeMutationsOfJustSeparators(string packageUrl)
{
PackageURL purl = new(packageUrl);
if (purl.Name is not null && purl.Type is not null)
{
BaseProjectManager? manager = ProjectManagerFactory.ConstructPackageManager(purl, null);
if (manager is not null)
{
foreach ((string _, IList<Mutation> mutations) in manager.EnumerateSquatCandidates(purl)!)
{
if (mutations.Any(m =>
{
var mutatedPurl = new PackageURL(m.Mutated);
return mutatedPurl.Name.Length == 1 &&
!char.IsLetterOrDigit(mutatedPurl.Name[0]);
}))
{
Assert.Fail($"Found a mutation that's a separator.");
}
}
}
}
}

[DataTestMethod]
[DataRow("pkg:npm/i")]
[DataRow("pkg:npm/ts")]
[DataRow("pkg:nuget/d")]
[DataRow("pkg:pypi/python")]
public void DontMakeEmptyMutations(string packageUrl)
{
PackageURL purl = new(packageUrl);
if (purl.Name is not null && purl.Type is not null)
{
BaseProjectManager? manager = ProjectManagerFactory.ConstructPackageManager(purl, null);
if (manager is not null)
{
foreach ((string mutation, IList<Mutation> mutations) in manager.EnumerateSquatCandidates(purl)!)
{
try
{
var mutatedPurl = new PackageURL(mutation);
if (mutations.Any(m => mutatedPurl.Name.Length == 0))
{
Assert.Fail($"Found a mutation that's got an empty name.");
}
}
catch (MalformedPackageUrlException e)
{
Console.WriteLine(e);
throw;
}

}
}
}
}

[DataTestMethod]
[DataRow("pkg:npm/foo")]
[DataRow("pkg:npm/rx")]
Expand Down

0 comments on commit 730b367

Please sign in to comment.