diff --git a/README.md b/README.md
index 0b6a232..8dc681d 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
# Sec-literal
+

[](https://github.com/NodeSecure/sec-literal/commit-activity)
[![OpenSSF
@@ -29,47 +30,139 @@ $ yarn add @nodesecure/sec-literal
## Hex
### isHex(anyValue): boolean
+
Detect if the given string is an Hexadecimal value
+```js
+Hex.isHex('4e20') // true
+Hex.isHex(20) // false
+```
+
### isSafe(anyValue): boolean
+
Detect if the given string is a safe Hexadecimal value. The goal of this method is to eliminate false-positive.
```js
-Hex.isSafe("1234"); // true
-Hex.isSafe("abcdef"); // true
+Hex.isSafe('393d8') // true
+Hex.isSafe('7f196a64a870440000') // false
```
## Literal
### isLiteral(anyValue): boolean
+
+Detect if the given literal is a ESTree literal.
+
+```js
+const literalSample = createLiteral("hello world");
+Literal.isLiteral(literalSample); // true
+Literal.isLiteral("hello world!"); // false
+```
+
### toValue(anyValue): string
+
+Returns the value of the literal if the input is an ESTree literal else it returns the original input
+
+```js
+const literalSample = createLiteral("hello world");
+Literal.toValue(literalSample); // returns "hello world"
+```
+
### toRaw(anyValue): string
+
+Returns the raw value of literal if the literal is an ESTree literal else it returns the original input
+
+```js
+const literalSample = createLiteral("hello world", true);
+Literal.toRaw(literalSample); // returns "hello world"
+```
+
### defaultAnalysis(literalValue)
+Returns an object which indicates if the literal contains hexadecimal, unicode or base64 sequence if the input is an ESTree literal else it returns null
+
+```js
+const literalSample = createLiteral("hello world");
+Literal.toRaw(literalSample); // returns {hasHexadecimalSequence: null, hasUnicodeSequence: null, isBase64: null}
+```
+
## Utils
### isSvg(strValue): boolean
+Detect if a given string is an SVG.
+
+```js
+const SVG_HTML = ` `;
+Utils.isSvg(SVG_HTML); // true
+```
+
### isSvgPath(strValue): boolean
-Detect if a given string is a svg path or not.
+
+Detect if a given string is a svg path.
+
+```js
+Utils.isSvgPath("M150 0 L75 200 L225 200 Z"); // true
+Utils.isSvgPath("hi there!"); // false
+```
### stringCharDiversity(str): number
-Get the number of unique chars in a given string
+
+Get the number of unique chars in a given string.
+
+```js
+Utils.stringCharDiversity("hello"); // returns 4
+Utils.stringCharDiversity("hello", ["l"]); // returns 3
+Utils.stringCharDiversity("syntax"); // returns 6
+```
### stringSuspicionScore(str): number
-Analyze a given string an give it a suspicion score (higher than 1 or 2 mean that the string is highly suspect).
+
+Analyze a given string and give it a suspicion score (higher than 1 or 2 mean that the string is highly suspect).
+
+```js
+Utils.stringSuspicionScore("hello world"); // returns 0
+Utils.stringSuspicionScore(
+ "XoMFrxuRvgb6a7lip6uYd6sz13E4KooQYqiIL0ZQReukg8BqZwsjCeay"
+); // returns 1
+```
## Patterns
### commonStringPrefix(leftStr, rightStr): string | null
+
+Get the common string prefix (at the start) pattern
+
+```js
+Patterns.commonStringPrefix("boo", "foo"); // null
+Patterns.commonStringPrefix("bromance", "brother"); // "bro"
+```
+
### commonStringSuffix(leftStr, rightStr): string | null
+
+Get the common string suffixes (at the end) pattern.
+
+```js
+Patterns.commonStringSuffix("boo", "foo"); // oo
+Patterns.commonStringSuffix("bromance", "brother"); // null
+```
+
### commonHexadecimalPrefix(identifiersArray: string[])
+Return the number of one time occurences of hexadecimal prefixes and an object containing the list of prefixes and the number of occurences in a given array of hexadecimals.
+
+```js
+Patterns.commonHexadecimalPrefix(["_0x33bb79", "foo", "_0x3c0c55", "_0x1185d5"]); // returns { oneTimeOccurence: 1, prefix: { _0x: 3 } }
+```
## Contributors ✨
+
[](#contributors-)
+
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
@@ -90,4 +183,5 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
## License
+
MIT
diff --git a/src/hex.js b/src/hex.js
index cb0988c..c0ba0a7 100644
--- a/src/hex.js
+++ b/src/hex.js
@@ -25,7 +25,11 @@ export const CONSTANTS = Object.freeze({
/**
* @description detect if the given string is an Hexadecimal value
* @param {SecLiteral.Literal | string} anyValue
- * @returns {boolean}
+ * @returns boolean
+ *
+ * @example
+ * isHex('4e20') // true
+ * isHex(20) // false
*/
export function isHex(anyValue) {
const value = Literal.toValue(anyValue);
@@ -36,7 +40,11 @@ export function isHex(anyValue) {
/**
* @description detect if the given string is a safe Hexadecimal value
* @param {SecLiteral.Literal | string} anyValue
- * @returns {boolean}
+ * @returns boolean
+ *
+ * @example
+ * isSafe('393d8') // true
+ * isSafe('7f196a64a870440000') // false
*/
export function isSafe(anyValue) {
const rawValue = Literal.toRaw(anyValue);
diff --git a/src/literal.js b/src/literal.js
index 1937c07..30d213e 100644
--- a/src/literal.js
+++ b/src/literal.js
@@ -2,32 +2,58 @@
import isStringBase64 from "is-base64";
/**
+ * @description detect if the given literal is a ESTree literal.
* @param {SecLiteral.Literal | string} anyValue
- * @returns {string}
+ * @returns boolean
+ *
+ * @example
+ * const literalSample = createLiteral("hello world");
+ * Literal.isLiteral(literalSample); // true
+ * Literal.isLiteral("hello world!"); // false
*/
export function isLiteral(anyValue) {
- return typeof anyValue === "object" && "type" in anyValue && anyValue.type === "Literal";
+ return (
+ typeof anyValue === "object" &&
+ "type" in anyValue &&
+ anyValue.type === "Literal"
+ );
}
/**
+ * @description returns the value of the literal if the input is an ESTree literal else returns the original input.
* @param {SecLiteral.Literal | string} strOrLiteral
- * @returns {string}
+ * @returns string
+ *
+ * @example
+ * const literalSample = createLiteral("hello world");
+ * Literal.toValue(literalSample); // returns "hello world"
*/
export function toValue(strOrLiteral) {
return isLiteral(strOrLiteral) ? strOrLiteral.value : strOrLiteral;
}
/**
+ * @description returns the raw value of literal if the literal is an ESTree literal else returns the original input
* @param {SecLiteral.Literal | string} strOrLiteral
- * @returns {string}
+ * @returns string
+ *
+ * @example
+ * const literalSample = createLiteral("hello world", true);
+ * Literal.toRaw(literalSample); // returns "hello world"
+ *
*/
export function toRaw(strOrLiteral) {
return isLiteral(strOrLiteral) ? strOrLiteral.raw : strOrLiteral;
}
/**
+ * @description returns an object which indicates if the literal contains hexadecimal, unicode or base64 sequence if the input is an ESTree literal else it returns null
* @param {!SecLiteral.Literal} literalValue
* @returns {SecLiteral.LiteralDefaultAnalysis}
+ *
+ * @example
+ * const literalSample = createLiteral("hello world");
+ * Literal.toRaw(literalSample); // returns { hasHexadecimalSequence: null, hasUnicodeSequence: null, isBase64: null}
*/
export function defaultAnalysis(literalValue) {
if (!isLiteral(literalValue)) {
@@ -35,8 +61,12 @@ export function defaultAnalysis(literalValue) {
}
const hasRawValue = "raw" in literalValue;
- const hasHexadecimalSequence = hasRawValue ? /\\x[a-fA-F0-9]{2}/g.exec(literalValue.raw) !== null : null;
- const hasUnicodeSequence = hasRawValue ? /\\u[a-fA-F0-9]{4}/g.exec(literalValue.raw) !== null : null;
+ const hasHexadecimalSequence = hasRawValue
+ ? /\\x[a-fA-F0-9]{2}/g.exec(literalValue.raw) !== null
+ : null;
+ const hasUnicodeSequence = hasRawValue
+ ? /\\u[a-fA-F0-9]{4}/g.exec(literalValue.raw) !== null
+ : null;
const isBase64 = isStringBase64(literalValue.value, { allowEmpty: false });
return { hasHexadecimalSequence, hasUnicodeSequence, isBase64 };
diff --git a/src/patterns.js b/src/patterns.js
index 034654c..06db512 100644
--- a/src/patterns.js
+++ b/src/patterns.js
@@ -11,8 +11,8 @@ import * as Literal from "./literal.js";
* @returns {string | null}
*
* @example
- * commonStringPrefix("boo", "foo"); // null
- * commonStringPrefix("bromance", "brother"); // "bro"
+ * Patterns.commonStringPrefix("boo", "foo"); // null
+ * Patterns.commonStringPrefix("bromance", "brother"); // "bro"
*/
export function commonStringPrefix(leftAnyValue, rightAnyValue) {
const leftStr = Literal.toValue(leftAnyValue);
@@ -44,8 +44,8 @@ function reverseString(string) {
* @returns {string | null}
*
* @example
- * commonStringSuffix("boo", "foo"); // oo
- * commonStringSuffix("bromance", "brother"); // null
+ * Patterns.commonStringSuffix("boo", "foo"); // oo
+ * Patterns.commonStringSuffix("bromance", "brother"); // null
*/
export function commonStringSuffix(leftStr, rightStr) {
const commonPrefix = commonStringPrefix(
@@ -56,6 +56,15 @@ export function commonStringSuffix(leftStr, rightStr) {
return commonPrefix === null ? null : reverseString(commonPrefix);
}
+/**
+ * @description returns the number of one time occurences of hexadecimal prefixes and an object containing the prefixes and the number of occurences in a given array of hexadecimals.
+ * @param {!string} leftStr
+ * @param {!string} rightStr
+ * @returns {string | null}
+ *
+ * @example
+ * Patterns.commonHexadecimalPrefix(["_0x33bb79", "foo", "_0x3c0c55", "_0x1185d5"]); // returns { oneTimeOccurence: 1, prefix: { _0x: 3 } }
+ */
export function commonHexadecimalPrefix(identifiersArray) {
if (!Array.isArray(identifiersArray)) {
throw new TypeError("identifiersArray must be an Array");
@@ -71,8 +80,7 @@ export function commonHexadecimalPrefix(identifiersArray) {
if (commonStr === cp || commonStr.startsWith(cp)) {
prefix.add(cp);
- }
- else if (cp.startsWith(commonStr)) {
+ } else if (cp.startsWith(commonStr)) {
prefix.delete(cp);
prefix.add(commonStr, count + 1);
}
@@ -93,6 +101,6 @@ export function commonHexadecimalPrefix(identifiersArray) {
return {
oneTimeOccurence,
- prefix: Object.fromEntries(prefix)
+ prefix: Object.fromEntries(prefix),
};
}
diff --git a/src/utils.js b/src/utils.js
index e77be2d..bd3df82 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -6,24 +6,34 @@ import stringWidth from "string-width";
import { toValue } from "./literal.js";
/**
+ * @description detect if a given string is an SVG.
* @param {SecLiteral.Literal | string} strOrLiteral
- * @returns {boolean}
+ * @returns boolean
+ *
+ * @example
+ * const SVG_HTML = ` `;
+ * Utils.isSvg(SVG_HTML); // true
*/
export function isSvg(strOrLiteral) {
try {
const value = toValue(strOrLiteral);
return isStringSvg(value) || isSvgPath(value);
- }
- catch {
+ } catch {
return false;
}
}
/**
- * @description detect if a given string is a svg path or not.
+ * @description detect if a given string is a svg path.
* @param {!string} str svg path literal
- * @returns {boolean}
+ * @returns boolean
+ *
+ * @example
+ * Utils.isSvgPath("M150 0 L75 200 L225 200 Z"); // true
+ * Utils.isSvgPath("hi there!"); // false
*/
export function isSvgPath(str) {
if (typeof str !== "string") {
@@ -31,13 +41,17 @@ export function isSvgPath(str) {
}
const trimStr = str.trim();
- return trimStr.length > 4 && /^[mzlhvcsqta]\s*[-+.0-9][^mlhvzcsqta]+/i.test(trimStr) && /[\dz]$/i.test(trimStr);
+ return (
+ trimStr.length > 4 &&
+ /^[mzlhvcsqta]\s*[-+.0-9][^mlhvzcsqta]+/i.test(trimStr) &&
+ /[\dz]$/i.test(trimStr)
+ );
}
/**
* @description detect if a given string is a morse value.
* @param {!string} str any string value
- * @returns {boolean}
+ * @returns boolean
*/
export function isMorse(str) {
return /^[.-]{1,5}(?:[\s\t]+[.-]{1,5})*(?:[\s\t]+[.-]{1,5}(?:[\s\t]+[.-]{1,5})*)*$/g.test(str);
@@ -45,7 +59,7 @@ export function isMorse(str) {
/**
* @param {!string} str any string value
- * @returns {string}
+ * @returns string
*/
export function escapeRegExp(str) {
return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
@@ -55,7 +69,12 @@ export function escapeRegExp(str) {
* @description Get the number of unique chars in a given string
* @param {!string} str string
* @param {string[]} [charsToExclude=[]]
- * @returns {number}
+ * @returns number
+ *
+ * @example
+ * Utils.stringCharDiversity("hello"); // returns 4
+ * Utils.stringCharDiversity("hello", ["l"]); // returns 3
+ * Utils.stringCharDiversity("syntax"); // returns 6
*/
export function stringCharDiversity(str, charsToExclude = []) {
const data = new Set(str);
@@ -71,9 +90,15 @@ const kMinUnsafeStringLenThreshold = 200;
const kScoreStringLengthThreshold = 750;
/**
- * @description Analyze a given string an give it a suspicion score (higher than 1 or 2 mean that the string is highly suspect).
+ * @description Analyze a given string and give it a suspicion score (higher than 1 or 2 mean that the string is highly suspect).
* @param {!string} str string to analyze
- * @returns {number}
+ * @returns number
+ *
+ * @example
+ * Utils.stringSuspicionScore("hello world"); // returns 0
+ * Utils.stringSuspicionScore(
+ * "XoMFrxuRvgb6a7lip6uYd6sz13E4KooQYqiIL0ZQReukg8BqZwsjCeay"
+ * ); // returns 1
*/
export function stringSuspicionScore(str) {
const strLen = stringWidth(str);
@@ -82,12 +107,16 @@ export function stringSuspicionScore(str) {
}
const includeSpace = str.includes(" ");
- const includeSpaceAtStart = includeSpace ? str.slice(0, kMaxSafeStringLen).includes(" ") : false;
+ const includeSpaceAtStart = includeSpace
+ ? str.slice(0, kMaxSafeStringLen).includes(" ")
+ : false;
let suspectScore = includeSpaceAtStart ? 0 : 1;
if (strLen > kMinUnsafeStringLenThreshold) {
suspectScore += Math.ceil(strLen / kScoreStringLengthThreshold);
}
- return stringCharDiversity(str) >= kMaxSafeStringCharDiversity ? suspectScore + 2 : suspectScore;
+ return stringCharDiversity(str) >= kMaxSafeStringCharDiversity
+ ? suspectScore + 2
+ : suspectScore;
}