Skip to content

Commit

Permalink
Add new AddContainingCharacterMutator(#411)
Browse files Browse the repository at this point in the history
AddContainingCharacterMutator helps with scenarios of going from squat candidate, to popular package name, and then confirming that said squat candidate is a squat on the popular package.
  • Loading branch information
jpinz authored Mar 21, 2023
1 parent 1d0a6e8 commit d064406
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/oss-find-squats-lib/MutateExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public static class MutateExtension
internal static IEnumerable<IMutator> BaseMutators { get; } = new List<IMutator>()
{
new AddCharacterMutator(),
new AddContainingCharacterMutator(),
new AsciiHomoglyphMutator(),
new BitFlipMutator(),
new CloseLettersMutator(),
Expand Down
38 changes: 38 additions & 0 deletions src/oss-find-squats-lib/Mutators/AddContainingCharacterMutator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.

namespace Microsoft.CST.OpenSource.FindSquats.Mutators
{
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Generates mutations for adding a character in the string, where the character being added already exists in the string.
/// </summary>
/// <example>requests -> reuquests. But won't generate something like requests -> regquests</example>
public class AddContainingCharacterMutator : IMutator
{
public MutatorType Kind { get; } = MutatorType.AddContainingCharacter;

public IEnumerable<Mutation> Generate(string arg)
{
var chars = arg.Distinct().ToArray();
for (int i = 0; i < arg.Length+1; i++)
{
// Can't add a character before an @ for a scoped package.
if (i == 0 && arg[i] == '@')
{
continue;
}

foreach (var c in chars)
{
yield return new Mutation(
mutated: $"{arg[..i]}{c}{arg[i..]}",
original: arg,
mutator: Kind,
reason: $"Containing Character Added: {c}");
}
}
}
}
}
4 changes: 4 additions & 0 deletions src/oss-find-squats-lib/Mutators/MutatorType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public enum MutatorType
/// </summary>
AddCharacter,
/// <summary>
/// The <see cref="AddContainingCharacterMutator"/> mutator.
/// </summary>
AddContainingCharacter,
/// <summary>
/// The <see cref="AsciiHomoglyphMutator"/> mutator.
/// </summary>
AsciiHomoglyph,
Expand Down
35 changes: 35 additions & 0 deletions src/oss-tests/FindSquatsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,41 @@ public async Task NewtonsoftMutations_Succeeds_Async()
CollectionAssert.AreEquivalent(squattingPackages, resultingMutationNames);
}

[TestMethod]
public async Task RequestsMutations_Succeeds_Async()
{
// arrange
PackageURL requests = new("pkg:pypi/[email protected]");

string[] squattingPackages = new[]
{
"pkg:pypi/reuquests", // AddContainingCharacter
"pkg:pypi/requestss", // DoubleHit
"pkg:pypi/reqests", // RemovedCharacter
"pkg:pypi/request", // RemovedCharacter
"pkg:pypi/requets", // RemovedCharacter
};

IHttpClientFactory httpClientFactory =
FindSquatsHelper.SetupHttpCalls(purl: requests, validSquats: squattingPackages);

IManagerPackageActions<NuGetPackageVersionMetadata> packageActions = PackageActionsHelper<NuGetPackageVersionMetadata>.SetupPackageActions(requests, validSquats: squattingPackages) ?? throw new InvalidOperationException();
Dictionary<string, ProjectManagerFactory.ConstructProjectManager> overrideDict = ProjectManagerFactory.GetDefaultManagers(httpClientFactory);

overrideDict[NuGetProjectManager.Type] = directory =>
new NuGetProjectManager(directory, packageActions, httpClientFactory);

FindPackageSquats findPackageSquats = new(new ProjectManagerFactory(overrideDict), requests);

// act
IDictionary<string, IList<Mutation>>? squatCandidates = findPackageSquats.GenerateSquatCandidates();
List<FindPackageSquatResult> existingMutations = await findPackageSquats.FindExistingSquatsAsync(squatCandidates, new MutateOptions(){UseCache = false}).ToListAsync();
Assert.IsNotNull(existingMutations);
Assert.IsTrue(existingMutations.Any());
string[] resultingMutationNames = existingMutations.Select(m => m.MutatedPackageUrl.ToString()).ToArray();
CollectionAssert.AreEquivalent(squattingPackages, resultingMutationNames);
}

[TestMethod]
public async Task ScopedPackage_Succeeds_Async()
{
Expand Down

0 comments on commit d064406

Please sign in to comment.