Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare major 8 #297

Merged
merged 3 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 9 additions & 155 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ The analysis will return: `http` (in try), `crypto`, `util` and `fs`.
> [!TIP]
> There is also a lot of suspicious code example in the `./examples` cases directory. Feel free to try the tool on these files.

## API

- [AstAnalyser](./docs/api/AstAnalyser.md)
- [EntryFilesAnalyser](./docs/api/EntryFilesAnalyser.md)

## Warnings

This section describes how use `warnings` export.
Expand Down Expand Up @@ -118,7 +123,7 @@ import * as i18n from "@nodesecure/i18n";
console.log(i18n.getTokenSync(jsxray.warnings["parsing-error"].i18n));
```

## Warnings Legends
### Legends

This section describe all the possible warnings returned by JSXRay. Click on the warning **name** for additional information and examples.

Expand All @@ -131,161 +136,10 @@ This section describe all the possible warnings returned by JSXRay. Click on the
| [encoded-literal](./docs/encoded-literal.md) | ❌ | An encoded literal has been detected (it can be an hexa value, unicode sequence or a base64 string) |
| [short-identifiers](./docs/short-identifiers.md) | ❌ | This mean that all identifiers has an average length below 1.5. |
| [suspicious-literal](./docs/suspicious-literal.md) | ❌ | A suspicious literal has been found in the source code. |
| [suspicious-file](./docs/suspicious-file.md) | ✔️ | A suspicious file with more than ten encoded-literal in it |
| [suspicious-file](./docs/suspicious-file.md) | | A suspicious file with more than ten encoded-literal in it |
| [obfuscated-code](./docs/obfuscated-code.md) | ✔️ | There's a very high probability that the code is obfuscated. |
| [weak-crypto](./docs/weak-crypto.md) | ✔️ | The code probably contains a weak crypto algorithm (md5, sha1...) |
| [shady-link](./docs/shady-link.md) | ✔️ | The code contains shady/unsafe link |

## Custom Probes

You can also create custom probes to detect specific pattern in the code you are analyzing.

A probe is a pair of two functions (`validateNode` and `main`) that will be called on each node of the AST. It will return a warning if the pattern is detected.
Below a basic probe that detect a string assignation to `danger`:

```ts
export const customProbes = [
{
name: "customProbeUnsafeDanger",
validateNode: (node, sourceFile) => [
node.type === "VariableDeclaration" && node.declarations[0].init.value === "danger"
],
main: (node, options) => {
const { sourceFile, data: calleeName } = options;
if (node.declarations[0].init.value === "danger") {
sourceFile.addWarning("unsafe-danger", calleeName, node.loc);

return ProbeSignals.Skip;
}

return null;
}
}
];
```

You can pass an array of probes to the `runASTAnalysis/runASTAnalysisOnFile` functions as `options`, or directly to the `AstAnalyser` constructor.

| Name | Type | Description | Default Value |
|------------------|----------------------------------|-----------------------------------------------------------------------|-----------------|
| `customParser` | `SourceParser \| undefined` | An optional custom parser to be used for parsing the source code. | `JsSourceParser` |
| `customProbes` | `Probe[] \| undefined` | An array of custom probes to be used during AST analysis. | `[]` |
| `skipDefaultProbes` | `boolean \| undefined` | If `true`, default probes will be skipped and only custom probes will be used. | `false` |


Here using the example probe upper:

```ts
import { AstAnalyser } from "@nodesecure/js-x-ray";

// add your customProbes here (see example above)

const scanner = new AstAnalyser({
customProbes,
skipDefaultProbes: true
});

const result = scanner.analyse("const danger = 'danger';");

console.log(result);
```

Result:

```sh
✗ node example.js
{
idsLengthAvg: 0,
stringScore: 0,
warnings: [ { kind: 'unsafe-danger', location: [Array], source: 'JS-X-Ray' } ],
dependencies: Map(0) {},
isOneLineRequire: false
}
```

Congrats, you have created your first custom probe! 🎉

> [!TIP]
> Check the types in [index.d.ts](index.d.ts) and [types/api.d.ts](types/api.d.ts) for more details about the `options`

## API

- [AstAnalyser](./docs/api/AstAnalyser.md)
- [EntryFilesAnalyser](./docs/api/EntryFilesAnalyser.md)

Legacy APIs waiting to be deprecated;

<details>
<summary>runASTAnalysis(str: string, options?: RuntimeOptions & AstAnalyserOptions): Report</summary>

```ts
interface RuntimeOptions {
module?: boolean;
removeHTMLComments?: boolean;
isMinified?: boolean;
initialize?: (sourceFile: SourceFile) => void;
finalize?: (sourceFile: SourceFile) => void;
}
```

```ts
interface AstAnalyserOptions {
customParser?: SourceParser;
customProbes?: Probe[];
skipDefaultProbes?: boolean;
}
```

The method take a first argument which is the code you want to analyse. It will return a Report Object:

```ts
interface Report {
dependencies: ASTDeps;
warnings: Warning[];
idsLengthAvg: number;
stringScore: number;
isOneLineRequire: boolean;
}
```

</details>

<details>
<summary>runASTAnalysisOnFile(pathToFile: string, options?: RuntimeFileOptions & AstAnalyserOptions): Promise< ReportOnFile ></summary>

```ts
interface RuntimeFileOptions {
module?: boolean;
removeHTMLComments?: boolean;
packageName?: string;
initialize?: (sourceFile: SourceFile) => void;
finalize?: (sourceFile: SourceFile) => void;
}
```

```ts
interface AstAnalyserOptions {
customParser?: SourceParser;
customProbes?: Probe[];
skipDefaultProbes?: boolean;
}
```

Run the SAST scanner on a given JavaScript file.

```ts
export type ReportOnFile = {
ok: true,
warnings: Warning[];
dependencies: ASTDeps;
isMinified: boolean;
} | {
ok: false,
warnings: Warning[];
}
```

</details>
| [weak-crypto](./docs/weak-crypto.md) | ❌ | The code probably contains a weak crypto algorithm (md5, sha1...) |
| [shady-link](./docs/shady-link.md) | ❌ | The code contains shady/unsafe link |

## Workspaces

Expand Down
93 changes: 89 additions & 4 deletions docs/api/AstAnalyser.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,30 @@ declare class AstAnalyser {
constructor(options?: AstAnalyserOptions);
analyse: (str: string, options?: RuntimeOptions) => Report;
analyseFile(pathToFile: string, options?: RuntimeFileOptions): Promise<ReportOnFile>;
analyseFileSync(pathToFile: string, options?: RuntimeFileOptions): ReportOnFile;
}
```

The `analyseFile` method is a superset of `analyse` with the ability to read the file on the local filesystem with additional features like detecting if the file is ESM or CJS.
The `analyseFile` and `analyseFileSync` methods is a superset of `analyse` with the ability to read the file on the local filesystem with additional features like detecting if the file is ESM/CJS (using the extension).

```ts
interface RuntimeOptions {
/**
* @default true
*/
module?: boolean;
/**
* @default false
*/
removeHTMLComments?: boolean;
/**
* @default false
*/
isMinified?: boolean;
initialize?: (sourceFile: SourceFile) => void;
finalize?: (sourceFile: SourceFile) => void;
}

interface Report {
dependencies: Map<string, Dependency>;
warnings: Warning[];
Expand All @@ -65,9 +83,7 @@ type ReportOnFile = {
}
```

## Examples

### `initialize`/`finalize` Hooks
### Hooks

The `analyse` method allows for the integration of two hooks: `initialize` and `finalize`.
These hooks are triggered before and after the analysis process, respectively.
Expand All @@ -90,3 +106,72 @@ scanner.analyse("const foo = 'bar';", {
}
});
```

## Custom Probes

You can also create custom probes to detect specific pattern in the code you are analyzing.

A probe is a pair of two functions (`validateNode` and `main`) that will be called on each node of the AST. It will return a warning if the pattern is detected.

Below a basic probe that detect a string assignation to `danger`:

```ts
export const customProbes = [
{
name: "customProbeUnsafeDanger",
validateNode: (node, sourceFile) => [
node.type === "VariableDeclaration" && node.declarations[0].init.value === "danger"
],
main: (node, options) => {
const { sourceFile, data: calleeName } = options;
if (node.declarations[0].init.value === "danger") {
sourceFile.addWarning("unsafe-danger", calleeName, node.loc);

return ProbeSignals.Skip;
}

return null;
}
}
];
```

You can pass an array of probes to the `AstAnalyser` constructor.

| Name | Type | Description | Default Value |
|---|---|---|---|
| **customParser** | `SourceParser \| undefined` | An optional custom parser to be used for parsing the source code. | `JsSourceParser` |
| **customProbes** | `Probe[] \| undefined` | An array of custom probes to be used during AST analysis. | `[]` |
| **skipDefaultProbes** | `boolean \| undefined` | If **true**, default probes will be skipped and only custom probes will be used. | `false` |

Here using the example probe upper:

```ts
import { AstAnalyser } from "@nodesecure/js-x-ray";

// add your customProbes here (see example above)

const scanner = new AstAnalyser({
customProbes,
skipDefaultProbes: true
});

const result = scanner.analyse("const danger = 'danger';");

console.log(result);
```

Result:

```sh
✗ node example.js
{
idsLengthAvg: 0,
stringScore: 0,
warnings: [ { kind: 'unsafe-danger', location: [Array], source: 'JS-X-Ray' } ],
dependencies: Map(0) {},
isOneLineRequire: false
}
```

Congrats, you have created your first custom probe! 🎉
4 changes: 2 additions & 2 deletions docs/shady-link.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Shady link
| Code | Severity | i18n | Experimental |
| --- | --- | --- | :-: |
| shady-link | `Warning` | `sast_warnings.shady_link` | ✔️ |
| shady-link | `Warning` | `sast_warnings.shady_link` | |

## Introduction

Expand Down Expand Up @@ -36,4 +36,4 @@ const IPv6URL = "http://2444:1130:80:2aa8:c313:150d:b8cf:c321/script";

> [!IMPORTANT]\
> Credit goes to the [guarddog](https://github.dev/DataDog/guarddog) team.\
> Credit goes to the [ietf.org](https://www.ietf.org/rfc/rfc3986.txt).
> Credit goes to the [ietf.org](https://www.ietf.org/rfc/rfc3986.txt).
2 changes: 1 addition & 1 deletion docs/suspicious-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

| Code | Severity | i18n | Experimental |
| --- | --- | --- | :-: |
| suspicious-file | `Critical` | `sast_warnings.suspicious_file` | ✔️ |
| suspicious-file | `Critical` | `sast_warnings.suspicious_file` | |

## Introduction

Expand Down
2 changes: 1 addition & 1 deletion docs/weak-crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

| Code | Severity | i18n | Experimental |
| --- | --- | --- | :-: |
| weak-crypto | `Information` | `sast_warnings.weak_crypto` | ✔️ |
| weak-crypto | `Information` | `sast_warnings.weak_crypto` | |

## Introduction

Expand Down
4 changes: 0 additions & 4 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import {

SourceParser,
JsSourceParser,
runASTAnalysis,
runASTAnalysisOnFile,
Report,
ReportOnFile,
RuntimeFileOptions,
Expand All @@ -34,8 +32,6 @@ export {
EntryFilesAnalyserOptions,
JsSourceParser,
SourceParser,
runASTAnalysis,
runASTAnalysisOnFile,
Report,
ReportOnFile,
RuntimeFileOptions,
Expand Down
Loading
Loading