Skip to content

Commit

Permalink
Added split.atXxx
Browse files Browse the repository at this point in the history
  • Loading branch information
robtimus committed Sep 17, 2023
1 parent 320c91f commit f5efdf3
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./obfuscators";
export * from "./propertyObfuscator";
export * from "./headerObfuscator";
export * from "./splitPoint";
118 changes: 118 additions & 0 deletions src/splitPoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { Obfuscator, obfuscateCustom } from "./obfuscators";

/**
* A point in a string to split obfuscation.
* Like {@link Obfuscator.untilLength}, this can be used to combine obfuscators. For instance, to obfuscate email addresses:
* Unlike {@link Obfuscator.untilLength} it's not possible to chain splitting, but it's of course possible to nest it.
* @example
* const localPartObfuscator = obfuscatePortion({
* keepAtStart: 1,
* keepAtEnd: 1,
* fixedTotalLength: 8
* });
* const domainObfuscator = obfuscateNone;
* const obfuscator = split.atFirst('@').to(localPartObfuscator, domainObfuscator);
* // Everything before @ will be obfuscated using localPartObfuscator, everything after @ will not be obfuscated
* // Example input: test@example.org
* // Example output: t******t@example.org
* @example
* const localPartObfuscator = obfuscatePortion({
* keepAtStart: 1,
* keepAtEnd: 1,
* fixedTotalLength: 8
* });
* const domainObfuscator = split.atLast('.').to(obfuscateAll(), obfuscateNone);
* const obfuscator = split.atFirst('@').split(localPartObfuscator, domainObfuscator);
* // Everything before @ will be obfuscated using localPartObfuscator, everything after @ will be obfuscated until the last dot
* // Example input: test@example.org
* // Example output: t******t@*******.org
*/
export interface SplitPoint {
/**
* Creates an obfuscator that splits obfuscation at this split point.
* The part of the string before the split point will be obfuscated by one obfuscator, the part after the split point by another.
* @param beforeSplitPoint The obfuscator to use before the split point.
* @param afterSplitPoint The obfuscator to use after the split point.
* @return The created obfuscator.
*/
to(beforeSplitPoint: Obfuscator, afterSplitPoint: Obfuscator): Obfuscator;
}

export interface SplitPoints {
/**
* Creates a new split point that splits at the first occurrence of a string.
* This split point is exclusive; the string itself will not be obfuscated.
*/
atFirst(s: string): SplitPoint;
/**
* Creates a new split point that splits at the last occurrence of a string.
* This split point is exclusive; the string itself will not be obfuscated.
*/
atLast(s: string): SplitPoint;
/**
* Creates a new split point that splits at a specific occurrence of a string.
* This split point is exclusive; the string itself will not be obfuscated.
* @param occurrence The zero-based occurrence of the string to split at.
*/
atNth(s: string, occurrence: number): SplitPoint;
}

function atFirst(s: string): SplitPoint {
function splitStart(text: string): number {
const index = text.indexOf(s);
return index == -1 ? text.length : index;
}
return splitPoint(splitStart, s.length);
}

function atLast(s: string): SplitPoint {
function splitStart(text: string): number {
const index = text.lastIndexOf(s);
return index == -1 ? text.length : index;
}
return splitPoint(splitStart, s.length);
}

function atNth(s: string, occurrence: number): SplitPoint {
if (occurrence < 0) {
throw new Error(`${occurrence} < 0`);
}
function splitStart(text: string): number {
let index = text.indexOf(s, 0);
for (let i = 1; i <= occurrence && index != -1; i++) {
index = text.indexOf(s, index + 1);
}
return index == -1 ? text.length : index;
}
return splitPoint(splitStart, s.length);
}

function splitPoint(splitStart: (text: string) => number, splitLength: number): SplitPoint {
return {
to(beforeSplitPoint, afterSplitPoint) {
return splitObfuscator(splitStart, splitLength, beforeSplitPoint, afterSplitPoint);
},
};
}

function splitObfuscator(splitStart: (text: string) => number, splitLength: number, beforeSplitPoint: Obfuscator, afterSplitPoint: Obfuscator): Obfuscator {
const obfuscate = (text: string): string => {
const splitStartIndex = splitStart(text);
if (splitStartIndex === text.length) {
return beforeSplitPoint(text);
}
return [
beforeSplitPoint(text.substring(0, splitStartIndex)),
text.substring(splitStartIndex, splitStartIndex + splitLength),
afterSplitPoint(text.substring(splitStartIndex + splitLength)),
].join("");
};
return obfuscateCustom(obfuscate);
}

export const split: SplitPoints = {
atFirst,
atLast,
atNth,
};
Object.freeze(split);
102 changes: 102 additions & 0 deletions test/splitPoint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { obfuscateAll, obfuscateWithFixedLength, obfuscateWithFixedValue, split } from "../src";

describe("split", () => {
describe("atFirst('@')", () => {
describe("to(obfuscateAll(), obfuscateWithFixedLength(5))", () => {
const obfuscator = split.atFirst("@").to(obfuscateAll(), obfuscateWithFixedLength(5));
const cases = [
["test", "****"],
["test@", "****@*****"],
["test@example.org", "****@*****"],
["test@example.org@", "****@*****"],
["test@example.org@localhost", "****@*****"],
["", ""],
];
it.each(cases)("applied to '%s' should be '%s'", (text, expected) => {
const obfuscated = obfuscator(text);
expect(obfuscated).toBe(expected);
});
});
});

describe("atLast('@')", () => {
describe("to(obfuscateAll(), obfuscateWithFixedLength(5))", () => {
const obfuscator = split.atLast("@").to(obfuscateAll(), obfuscateWithFixedLength(5));
const cases = [
["test", "****"],
["test@", "****@*****"],
["test@example.org", "****@*****"],
["test@example.org@", "****************@*****"],
["test@example.org@localhost", "****************@*****"],
["", ""],
];
it.each(cases)("applied to '%s' should be '%s'", (text, expected) => {
const obfuscated = obfuscator(text);
expect(obfuscated).toBe(expected);
});
});
});

test("atNth('.', -1)", () => {
expect(() => split.atNth(".", -1)).toThrowError("-1 < 0");
});

describe("atNth('.', 0)", () => {
describe("to(obfuscateWithFixedValue('xxx'), obfuscateWithFixedLength(5))", () => {
const obfuscator = split.atNth(".", 0).to(obfuscateWithFixedValue("xxx"), obfuscateWithFixedLength(5));
const cases = [
["alpha", "xxx"],
["alpha.", "xxx.*****"],
["alpha.bravo", "xxx.*****"],
["alpha.bravo.charlie", "xxx.*****"],
["alpha.bravo.charlie.", "xxx.*****"],
["alpha.bravo.charlie.delta", "xxx.*****"],
["alpha.bravo.charlie.delta.echo", "xxx.*****"],
["........", "xxx.*****"],
];
it.each(cases)("applied to '%s' should be '%s'", (text, expected) => {
const obfuscated = obfuscator(text);
expect(obfuscated).toBe(expected);
});
});
});

describe("atNth('.', 1)", () => {
describe("to(obfuscateWithFixedValue('xxx'), obfuscateWithFixedLength(5))", () => {
const obfuscator = split.atNth(".", 1).to(obfuscateWithFixedValue("xxx"), obfuscateWithFixedLength(5));
const cases = [
["alpha", "xxx"],
["alpha.bravo", "xxx"],
["alpha.bravo.", "xxx.*****"],
["alpha.bravo.charlie", "xxx.*****"],
["alpha.bravo.charlie.", "xxx.*****"],
["alpha.bravo.charlie.delta", "xxx.*****"],
["alpha.bravo.charlie.delta.echo", "xxx.*****"],
["........", "xxx.*****"],
];
it.each(cases)("applied to '%s' should be '%s'", (text, expected) => {
const obfuscated = obfuscator(text);
expect(obfuscated).toBe(expected);
});
});
});

describe("atNth('.', 2)", () => {
describe("to(obfuscateWithFixedValue('xxx'), obfuscateWithFixedLength(5))", () => {
const obfuscator = split.atNth(".", 2).to(obfuscateWithFixedValue("xxx"), obfuscateWithFixedLength(5));
const cases = [
["alpha", "xxx"],
["alpha.bravo", "xxx"],
["alpha.bravo.charlie", "xxx"],
["alpha.bravo.charlie.", "xxx.*****"],
["alpha.bravo.charlie.delta", "xxx.*****"],
["alpha.bravo.charlie.delta.echo", "xxx.*****"],
["........", "xxx.*****"],
];
it.each(cases)("applied to '%s' should be '%s'", (text, expected) => {
const obfuscated = obfuscator(text);
expect(obfuscated).toBe(expected);
});
});
});
});

0 comments on commit f5efdf3

Please sign in to comment.