-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
221 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
}); | ||
}); | ||
}); |