Skip to content

Commit 2eb72c9

Browse files
authored
Merge pull request #36 from NodeSecure/documentation-enhancement
Documentation enhancement
2 parents 5c6036f + 67c2fe7 commit 2eb72c9

22 files changed

+397
-113
lines changed

README.md

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ JavaScript AST analysis. This package has been created to export the [Node-Secur
1010

1111
The goal is to quickly identify dangerous code and patterns for developers and Security researchers. Interpreting the results of this tool will still require you to have a set of security notions.
1212

13-
> 💖 I have no particular background in security. I'm simply becoming more and more interested and passionate about static code analysis. But I would be more than happy to learn that my work can help prevent potential future attacks (or leaks).
13+
> **Note** I have no particular background in security. I'm simply becoming more and more interested and passionate about static code analysis. But I would be more than happy to learn that my work can help prevent potential future attacks (or leaks).
1414
1515
## Goals
1616
The objective of the project is to successfully detect all potentially suspicious JavaScript codes.. The target is obviously codes that are added or injected for malicious purposes..
@@ -71,7 +71,7 @@ console.log(warnings);
7171

7272
The analysis will return: `http` (in try), `crypto`, `util` and `fs`.
7373

74-
> ⚠️ There is also a lot of suspicious code example in the root cases directory. Feel free to try the tool on these files.
74+
> **Warning** There is also a lot of suspicious code example in the `./examples` cases directory. Feel free to try the tool on these files.
7575
7676
## Warnings
7777

@@ -106,26 +106,24 @@ import * as i18n from "@nodesecure/i18n";
106106
console.log(i18n.getToken(jsxray.warnings.parsingError.i18n));
107107
```
108108

109-
## Warnings Legends (v2.0+)
109+
## Warnings Legends
110110

111-
> Node-secure versions equal or lower than 0.7.0 are no longer compatible with the warnings table below.
111+
> **Warning** versions of NodeSecure greather than v0.7.0 are no longer compatible with the warnings table below.
112112
113-
This section describe all the possible warnings returned by JSXRay.
113+
This section describe all the possible warnings returned by JSXRay. Click on the warning **name** for additional information and examples.
114114

115-
| name | description |
116-
| --- | --- |
117-
| parsing-error | An error occured when parsing the JavaScript code with meriyah. It mean that the conversion from string to AST as failed. If you encounter such an error, **please open an issue here**. |
118-
| unsafe-import | Unable to follow an import (require, require.resolve) statement/expr. |
119-
| unsafe-regex | A RegEx as been detected as unsafe and may be used for a ReDoS Attack. |
120-
| unsafe-stmt | Usage of dangerous statement like `eval()` or `Function("")`. |
121-
| unsafe-assign | Assignment of a protected global like `process` or `require`. |
122-
| encoded-literal | An encoded literal has been detected (it can be an hexa value, unicode sequence, base64 string etc) |
123-
| short-identifiers | This mean that all identifiers has an average length below 1.5. Only possible if the file contains more than 5 identifiers. |
124-
| suspicious-literal | This mean that the sum of suspicious score of all Literals is bigger than 3. |
125-
| obfuscated-code (**experimental**) | There's a very high probability that the code is obfuscated... |
126-
| weak-crypto (**experimental**) | The code probably contains a weak crypto algorithm ("md5...) |
127-
128-
> 👀 More details on warnings and their implementations [here](./WARNINGS.md)
115+
| name | experimental | description |
116+
| --- | :-: | --- |
117+
| [parsing-error](./docs/parsing-error.md) || The AST parser throw an error |
118+
| [unsafe-import](./docs/unsafe-import.md) || Unable to follow an import (require, require.resolve) statement/expr. |
119+
| [unsafe-regex](./docs/unsafe-regex.md) || A RegEx as been detected as unsafe and may be used for a ReDoS Attack. |
120+
| [unsafe-stmt](./docs//unsafe-stmt.md) || Usage of dangerous statement like `eval()` or `Function("")`. |
121+
| [unsafe-assign](./docs/unsafe-assign.md) || Assignment of a protected global like `process` or `require`. |
122+
| [encoded-literal](./docs/encoded-literal.md) || An encoded literal has been detected (it can be an hexa value, unicode sequence or a base64 string) |
123+
| [short-identifiers](./docs/short-identifiers.md) || This mean that all identifiers has an average length below 1.5. |
124+
| [suspicious-literal](./docs/suspicious-literal.md) || A suspicious literal has been found in the source code. |
125+
| [obfuscated-code](./docs/obfuscated-code.md) | ✔️ | There's a very high probability that the code is obfuscated. |
126+
| [weak-crypto](./docs/weak-crypto.md) | ✔️ | The code probably contains a weak crypto algorithm (md5, sha1...) |
129127

130128
## API
131129

@@ -153,6 +151,32 @@ interface Report {
153151

154152
</details>
155153

154+
<details>
155+
<summary>runASTAnalysisOnFile(pathToFile: string, options?: RuntimeFileOptions): Promise< ReportOnFile ></summary>
156+
157+
```ts
158+
interface RuntimeOptions {
159+
module?: boolean;
160+
isMinified?: boolean;
161+
}
162+
```
163+
164+
Run the SAST scanner on a given JavaScript file.
165+
166+
```ts
167+
export type ReportOnFile = {
168+
ok: true,
169+
warnings: Warning<BaseWarning>[];
170+
dependencies: ASTDeps;
171+
isMinified: boolean;
172+
} | {
173+
ok: false,
174+
warnings: Warning<BaseWarning>[];
175+
}
176+
```
177+
178+
</details>
179+
156180
157181
## Contributors ✨
158182

WARNINGS.md

Lines changed: 0 additions & 93 deletions
This file was deleted.

docs/encoded-literal.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Encoded literal
2+
3+
| Code | Severity | i18n | Experimental |
4+
| --- | --- | --- | :-: |
5+
| encoded-literal | `Information` | `sast_warnings.encoded_literal` ||
6+
7+
## Introduction
8+
9+
The SAST scanner assert all Literals in the tree and search for encoded values. JS-X-Ray currently supports three types of detection:
10+
- Hexadecimal sequence: `'\x72\x4b\x58\x6e\x75\x65\x38\x3d'`
11+
- Unicode sequence: `\u03B1`
12+
- Base64 encryption: `z0PgB0O=`
13+
14+
Hexadecimal and Unicode sequence are tested directly on the raw Literal provided by meriyah. For base64 detection we use the npm package [is-base64](https://github.com/miguelmota/is-base64).
15+
16+
Example of a JavaScript implementation:
17+
```js
18+
const hasHexadecimalSequence = /\\x[a-fA-F0-9]{2}/g.exec(node.raw) !== null;
19+
const hasUnicodeSequence = /\\u[a-fA-F0-9]{4}/g.exec(node.raw) !== null;
20+
const isBase64 = isStringBase64(node.value, { allowEmpty: false });
21+
```

docs/obfuscated-code.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Obfuscated code
2+
3+
| Code | Severity | i18n | Experimental |
4+
| --- | --- | --- | :-: |
5+
| obfuscated-code | `Critical` | `sast_warnings.obfuscated_code` | ✔️ |
6+
7+
## Introduction
8+
9+
An **experimental** warning capable of detecting obfuscation and sometimes the tool used. The scanner is capable to detect:
10+
11+
- [freejsobfuscator](http://www.freejsobfuscator.com/)
12+
- [jjencode](https://utf-8.jp/public/jjencode.html)
13+
- [jsfuck](http://www.jsfuck.com/)
14+
- [obfuscator.io](https://obfuscator.io/)
15+
- morse
16+
- [trojan source](https://trojansource.codes/)
17+
18+
Example of obfuscated code is in the root `examples` directory.
19+
20+
### Technical note
21+
A complete G.Drive document has been written to describe the patterns of obfuscation tools and some way of detecting them:
22+
23+
- [JSXRay - Patterns of obfuscated JavaScript code](https://docs.google.com/document/d/11ZrfW0bDQ-kd7Gr_Ixqyk8p3TGvxckmhFH3Z8dFoPhY/edit?usp=sharing)
24+
25+
> **Note** There is no frozen implementation and this is an early implementation
26+
27+
## Example
28+
29+
The following code uses Morse code to obfuscate its real intent. This was used in an attack and I find it quite funny so i implemented morse detection 😂.
30+
31+
```js
32+
function decodeMorse(morseCode) {
33+
var ref = {
34+
'.-': 'a',
35+
'-...': 'b',
36+
'-.-.': 'c',
37+
'-..': 'd',
38+
'.': 'e',
39+
'..-.': 'f',
40+
'--.': 'g',
41+
'....': 'h',
42+
'..': 'i',
43+
'.---': 'j',
44+
'-.-': 'k',
45+
'.-..': 'l',
46+
'--': 'm',
47+
'-.': 'n',
48+
'---': 'o',
49+
'.--.': 'p',
50+
'--.-': 'q',
51+
'.-.': 'r',
52+
'...': 's',
53+
'-': 't',
54+
'..-': 'u',
55+
'...-': 'v',
56+
'.--': 'w',
57+
'-..-': 'x',
58+
'-.--': 'y',
59+
'--..': 'z',
60+
'.----': '1',
61+
'..---': '2',
62+
'...--': '3',
63+
'....-': '4',
64+
'.....': '5',
65+
'-....': '6',
66+
'--...': '7',
67+
'---..': '8',
68+
'----.': '9',
69+
'-----': '0',
70+
};
71+
72+
return morseCode
73+
.split(' ')
74+
.map(a => a.split(' ').map(b => ref[b]).join(''))
75+
.join(' ');
76+
}
77+
78+
var decoded = decodeMorse(".-- --- .-. -.. .-- --- .-. -..");
79+
console.log(decoded);
80+
```

docs/parsing-error.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Parsing Error
2+
3+
| Code | Severity | i18n | Experimental |
4+
| --- | --- | --- | :-: |
5+
| ast-error | `Information` | `sast_warnings.ast_error` ||
6+
7+
## Introduction
8+
9+
Parsing Error is throw when the library [meriyah](https://github.com/meriyah/meriyah) fail to parse the javascript source code into an AST. But it can also happen when the AST analysis fails because we don't manage a case properly.
10+
11+
> **Note** If you are in the second case, please open an issue [here](https://github.com/NodeSecure/js-x-ray/issues)
12+
13+
## Example
14+
15+
```json
16+
{
17+
"kind": "parsing-error",
18+
"value": "[10:30]: Unexpected token: ','",
19+
"location": [[0,0],[0,0]],
20+
"file": "helpers\\asyncIterator.js"
21+
}
22+
```

docs/short-identifiers.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Short identifiers
2+
3+
| Code | Severity | i18n | Experimental |
4+
| --- | --- | --- | :-: |
5+
| short-identifiers | `Warning` | `sast_warnings.short_identifiers` ||
6+
7+
## Introduction
8+
9+
The SAST store in memory all Identifiers id so we are able later to sum the length of all ids. We are looking at several ESTree Node in the tree:
10+
- VariableDeclarator: `var boo;`
11+
- AssignmentExpression: `(boo = 5)`
12+
- FunctionDeclaration: `function boo() {}`
13+
- Property of ObjectExpression: `{ boo: 5 }`
14+
15+
However, we do not take into consideration the properties of Objects for this warning. The warning is generated only if:
16+
17+
- The file is not already declared as **Minified**.
18+
- There is more than **five** identifiers.
19+
- The sum of all identifiers name length is below `1.5`.
20+
21+
## Example
22+
23+
```json
24+
{
25+
"kind": "short-identifiers",
26+
"location": [[0,0], [0,0]],
27+
"value": 1.5,
28+
"file": "lib\\compile-env.js"
29+
}
30+
```

docs/suspicious-literal.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Suspicious literal
2+
3+
| Code | Severity | i18n | Experimental |
4+
| --- | --- | --- | :-: |
5+
| suspicious-literal | `Warning` | `sast_warnings.suspicious_literal` ||
6+
7+
## Introduction
8+
9+
Thats one of the most interesting JS-X-Ray warning. We designed it with the idea of detecting long strings of characters that are very common in malicious obfuscated/encrypted codes like in [smith-and-wesson-skimmer](https://badjs.org/posts/smith-and-wesson-skimmer/).
10+
11+
The basic idea is to say that any string longer than 45 characters with no space is very suspicious... Then we establish a suspicion score that will be incremented according to several criteria:
12+
13+
- if the string contains **space** in the first **45** characters then we set the score to `zero`, else we set the score to `one`.
14+
- if the string has more than **200 characters** then we add `1` to the score.
15+
- we add one to the score for each **750 characters**. So a length of __1600__ will add `two` to the score.
16+
- we add `two` point to the score if the string contains more than **70 unique characters**.
17+
18+
So it's possible for a string with more than 45 characters to come out with a score of zero if:
19+
- there is space in the first 45 characters of the string.
20+
- less than 70 unique characters.
21+
22+
The implementation is done in the [@nodesecure/sec-literal](https://github.com/NodeSecure/sec-literal/blob/main/src/utils.js) package and look like this:
23+
```js
24+
function stringCharDiversity(str, charsToExclude = []) {
25+
const data = new Set(str);
26+
charsToExclude.forEach((char) => data.delete(char));
27+
28+
return data.size;
29+
}
30+
31+
// ---
32+
const kMaxSafeStringLen = 45;
33+
const kMaxSafeStringCharDiversity = 70;
34+
const kMinUnsafeStringLenThreshold = 200;
35+
const kScoreStringLengthThreshold = 750;
36+
37+
function stringSuspicionScore(str) {
38+
const strLen = stringWidth(str);
39+
if (strLen < kMaxSafeStringLen) {
40+
return 0;
41+
}
42+
43+
const includeSpace = str.includes(" ");
44+
const includeSpaceAtStart = includeSpace ? str.slice(0, kMaxSafeStringLen).includes(" ") : false;
45+
46+
let suspectScore = includeSpaceAtStart ? 0 : 1;
47+
if (strLen > kMinUnsafeStringLenThreshold) {
48+
suspectScore += Math.ceil(strLen / kScoreStringLengthThreshold);
49+
}
50+
51+
return stringCharDiversity(str) >= kMaxSafeStringCharDiversity ? suspectScore + 2 : suspectScore;
52+
}
53+
```
54+
55+
> **Note** The warning is generated only if the sum of all scores exceeds **three**.

0 commit comments

Comments
 (0)