@rexymayderio/sentinel 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -6
- package/dist/analyzers/install-script-analyzer.js +2 -2
- package/dist/analyzers/install-script-analyzer.js.map +1 -1
- package/dist/analyzers/match-evidence.d.ts +6 -1
- package/dist/analyzers/match-evidence.d.ts.map +1 -1
- package/dist/analyzers/match-evidence.js +10 -3
- package/dist/analyzers/match-evidence.js.map +1 -1
- package/dist/analyzers/network-analyzer.d.ts.map +1 -1
- package/dist/analyzers/network-analyzer.js +85 -4
- package/dist/analyzers/network-analyzer.js.map +1 -1
- package/dist/analyzers/static-code-analyzer.d.ts.map +1 -1
- package/dist/analyzers/static-code-analyzer.js +4 -2
- package/dist/analyzers/static-code-analyzer.js.map +1 -1
- package/dist/analyzers/strip-comments.d.ts +9 -0
- package/dist/analyzers/strip-comments.d.ts.map +1 -0
- package/dist/analyzers/strip-comments.js +86 -0
- package/dist/analyzers/strip-comments.js.map +1 -0
- package/dist/analyzers/test-path.d.ts +7 -3
- package/dist/analyzers/test-path.d.ts.map +1 -1
- package/dist/analyzers/test-path.js +40 -25
- package/dist/analyzers/test-path.js.map +1 -1
- package/dist/cli/index.js +7 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/core/sentinel.d.ts +1 -0
- package/dist/core/sentinel.d.ts.map +1 -1
- package/dist/core/sentinel.js +28 -20
- package/dist/core/sentinel.js.map +1 -1
- package/dist/engine/default-policy.d.ts +5 -0
- package/dist/engine/default-policy.d.ts.map +1 -1
- package/dist/engine/default-policy.js.map +1 -1
- package/dist/engine/policy-engine.js +3 -3
- package/dist/engine/policy-engine.js.map +1 -1
- package/dist/engine/risk-calculator.d.ts +1 -5
- package/dist/engine/risk-calculator.d.ts.map +1 -1
- package/dist/engine/risk-calculator.js +2 -6
- package/dist/engine/risk-calculator.js.map +1 -1
- package/dist/mcp/server.js +4 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/report/report-generator.js +1 -1
- package/dist/report/report-generator.js.map +1 -1
- package/package.json +1 -1
- package/skills/sentinel/SKILL.md +83 -54
package/README.md
CHANGED
|
@@ -4,8 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
Sentinel sits between an AI agent (or a human) and any installer. Nothing installs until the target has been **acquired (download-only, never executed)**, **analyzed**, **risk-scored**, **policy-checked**, and **explicitly approved**.
|
|
6
6
|
|
|
7
|
-
```
|
|
8
|
-
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart TD
|
|
9
|
+
A[AI / User] --> B[Sentinel]
|
|
10
|
+
B --> C[Acquire]
|
|
11
|
+
C --> D[Analyze]
|
|
12
|
+
D --> E[Score]
|
|
13
|
+
E --> F[Policy]
|
|
14
|
+
F --> G[Approve]
|
|
15
|
+
G --> H[Install]
|
|
9
16
|
```
|
|
10
17
|
|
|
11
18
|
The installer never runs directly. Acquirers only download and read files; install scripts are never executed during analysis.
|
|
@@ -55,7 +62,7 @@ sentinel verify npm express --markdown
|
|
|
55
62
|
# Custom policy file
|
|
56
63
|
sentinel verify npm express --policy ./policy.json
|
|
57
64
|
|
|
58
|
-
#
|
|
65
|
+
# Scan test/fixture files with the full ruleset (default: secrets + malware only)
|
|
59
66
|
sentinel verify local ./my-project --score-tests
|
|
60
67
|
|
|
61
68
|
# CLI help
|
|
@@ -105,7 +112,7 @@ MCP tools: `verify_package`, `verify_repository`, `verify_skill`, `verify_mcp`,
|
|
|
105
112
|
|
|
106
113
|
### Agent Skill
|
|
107
114
|
|
|
108
|
-
Install the Sentinel agent skill so your AI assistant intercepts install requests, verifies via the MCP above, explains risks, and only installs after approval.
|
|
115
|
+
Install the [Sentinel agent skill](https://github.com/RexySaragih/sentinel/blob/master/skills/sentinel/SKILL.md) so your AI assistant intercepts install requests, verifies via the MCP above, explains risks, and only installs after approval.
|
|
109
116
|
|
|
110
117
|
Copy or symlink [skills/sentinel/SKILL.md](skills/sentinel/SKILL.md) into your agent's skills directory (e.g. `~/.cursor/skills/sentinel/SKILL.md` for Cursor).
|
|
111
118
|
|
|
@@ -139,7 +146,14 @@ Each finding has a severity. The Risk Calculator sums severity weights, subtract
|
|
|
139
146
|
|
|
140
147
|
Positive signals (verified publisher, long history, etc.) each subtract `5`, capped at `-30` total.
|
|
141
148
|
|
|
142
|
-
**Test/fixture code** is detected by path (`test/`, `tests/`, `__tests__/`, `fixtures/`, `*.test.*`, `*.spec.*`, etc.)
|
|
149
|
+
**Test/fixture code** is detected by path (`test/`, `tests/`, `__tests__/`, `fixtures/`, `*.test.*`, `*.spec.*`, etc.) and scanned with a **narrower ruleset** than production code. Everyday dev patterns (`child_process`, `spawn`, `eval`, dynamic imports, `rm -rf`, ...) are expected in tests and are **not** flagged there. Instead, test files are checked only for the things that genuinely matter in tests — the places malware likes to hide:
|
|
150
|
+
|
|
151
|
+
- **Leaked secrets** (AWS/GCP/OpenAI keys, private keys, tokens, ...)
|
|
152
|
+
- **Malware signatures**: remote payload delivery (`curl … | sh`, `wget … | sh`), crypto miners, UPX-packed blobs
|
|
153
|
+
- **Exfiltration / C2 channels**: Discord/Telegram webhooks, Pastebin, ngrok, Cloudflare tunnels, `.onion`, dynamic DNS
|
|
154
|
+
- **Prompt-injection attacks** hidden in fixtures
|
|
155
|
+
|
|
156
|
+
Findings that survive this filter are real, so they are reported (tagged `[test-file]` in terminal output, `isTest: true` in JSON) and counted at **full weight** in the risk score and policy. Use `--score-tests` (or set `scoreTestCodeFully: true` in policy) to scan test files with the full production ruleset instead.
|
|
143
157
|
|
|
144
158
|
The final score maps to a **risk level**:
|
|
145
159
|
|
|
@@ -246,7 +260,7 @@ Pass a JSON file via `--policy <file>` to override defaults (`src/engine/default
|
|
|
246
260
|
| `warnOnInstallScript` | boolean | `true` | Install scripts -> `WARN`. |
|
|
247
261
|
| `warnOnShellAccess` | boolean | `true` | Shell access -> `WARN`/`REQUIRE_APPROVAL`. |
|
|
248
262
|
| `allowOverrides` | boolean | `true` | Allow human overrides of non-`BLOCK` decisions. |
|
|
249
|
-
| `scoreTestCodeFully` | boolean | `false` | When `
|
|
263
|
+
| `scoreTestCodeFully` | boolean | `false` | When `false`, test/fixture files are scanned only for leaked secrets and malware signatures. When `true`, they are scanned with the full production ruleset. |
|
|
250
264
|
|
|
251
265
|
Example `policy.json`:
|
|
252
266
|
|
|
@@ -26,9 +26,9 @@ export class InstallScriptAnalyzer {
|
|
|
26
26
|
continue;
|
|
27
27
|
findings.push(createFinding({
|
|
28
28
|
category: 'install-script',
|
|
29
|
-
severity: '
|
|
29
|
+
severity: 'LOW',
|
|
30
30
|
title: `Install script: ${key}`,
|
|
31
|
-
description: `Package defines a ${key} script`,
|
|
31
|
+
description: `Package defines a ${key} script - it runs automatically on install`,
|
|
32
32
|
ruleId: `script-${key}`,
|
|
33
33
|
evidence: script,
|
|
34
34
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-script-analyzer.js","sourceRoot":"","sources":["../../src/analyzers/install-script-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,MAAM,mBAAmB,GAAG,CAAC,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AAC9F,MAAM,0BAA0B,GAAG;IACjC,EAAE,OAAO,EAAE,gCAAgC,EAAE,QAAQ,EAAE,UAAmB,EAAE,KAAK,EAAE,sCAAsC,EAAE;IAC3H,EAAE,OAAO,EAAE,gCAAgC,EAAE,QAAQ,EAAE,UAAmB,EAAE,KAAK,EAAE,sCAAsC,EAAE;IAC3H,EAAE,OAAO,EAAE,uCAAuC,EAAE,QAAQ,EAAE,MAAe,EAAE,KAAK,EAAE,0CAA0C,EAAE;IAClI,EAAE,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAe,EAAE,KAAK,EAAE,wBAAwB,EAAE;IACrF,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAiB,EAAE,KAAK,EAAE,4BAA4B,EAAE;IAC7F,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAe,EAAE,KAAK,EAAE,0BAA0B,EAAE;IACtF,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAiB,EAAE,KAAK,EAAE,uBAAuB,EAAE;IACnF,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAiB,EAAE,KAAK,EAAE,4BAA4B,EAAE;IAC5F,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,QAAiB,EAAE,KAAK,EAAE,+BAA+B,EAAE;IAClG,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,QAAiB,EAAE,KAAK,EAAE,+BAA+B,EAAE;CACnG,CAAC;AAEF,MAAM,OAAO,qBAAqB;IACvB,EAAE,GAAG,gBAAgB,CAAC;IAE/B,QAAQ,CAAC,GAAoB;QAC3B,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAoB;QAChC,MAAM,QAAQ,GAAG,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC;QAEpD,KAAK,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC;YACtC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,CAAC,MAAM;gBAAE,SAAS;YAEtB,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC;gBAC1B,QAAQ,EAAE,gBAAgB;gBAC1B,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"install-script-analyzer.js","sourceRoot":"","sources":["../../src/analyzers/install-script-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export declare const MAX_EVIDENCE_LENGTH = 120;
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Finds the first line matching `pattern`. Matching runs against `searchContent`
|
|
4
|
+
* (which may have comments masked out), while the returned evidence is taken from
|
|
5
|
+
* `displayContent` so the user still sees the real source line.
|
|
6
|
+
*/
|
|
7
|
+
export declare function findMatchingLine(searchContent: string, pattern: RegExp, displayContent?: string): {
|
|
3
8
|
line: number;
|
|
4
9
|
evidence: string;
|
|
5
10
|
} | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match-evidence.d.ts","sourceRoot":"","sources":["../../src/analyzers/match-evidence.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,wBAAgB,gBAAgB,CAC9B,
|
|
1
|
+
{"version":3,"file":"match-evidence.d.ts","sourceRoot":"","sources":["../../src/analyzers/match-evidence.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,cAAc,GAAE,MAAsB,GACrC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAchD"}
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
export const MAX_EVIDENCE_LENGTH = 120;
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Finds the first line matching `pattern`. Matching runs against `searchContent`
|
|
4
|
+
* (which may have comments masked out), while the returned evidence is taken from
|
|
5
|
+
* `displayContent` so the user still sees the real source line.
|
|
6
|
+
*/
|
|
7
|
+
export function findMatchingLine(searchContent, pattern, displayContent = searchContent) {
|
|
8
|
+
const lines = searchContent.split('\n');
|
|
9
|
+
const displayLines = displayContent.split('\n');
|
|
4
10
|
for (let i = 0; i < lines.length; i++) {
|
|
5
11
|
pattern.lastIndex = 0;
|
|
6
12
|
if (pattern.test(lines[i])) {
|
|
13
|
+
const source = displayLines[i] ?? lines[i];
|
|
7
14
|
return {
|
|
8
15
|
line: i + 1,
|
|
9
|
-
evidence:
|
|
16
|
+
evidence: source.trim().slice(0, MAX_EVIDENCE_LENGTH),
|
|
10
17
|
};
|
|
11
18
|
}
|
|
12
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"match-evidence.js","sourceRoot":"","sources":["../../src/analyzers/match-evidence.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEvC,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,
|
|
1
|
+
{"version":3,"file":"match-evidence.js","sourceRoot":"","sources":["../../src/analyzers/match-evidence.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEvC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,aAAqB,EACrB,OAAe,EACf,iBAAyB,aAAa;IAEtC,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,YAAY,GAAG,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;QACtB,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAE,CAAC;YAC5C,OAAO;gBACL,IAAI,EAAE,CAAC,GAAG,CAAC;gBACX,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC;aACtD,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzers/network-analyzer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"network-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzers/network-analyzer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AA2D/D,qBAAa,eAAgB,YAAW,QAAQ;IAC9C,QAAQ,CAAC,EAAE,aAAa;IAExB,QAAQ,IAAI,OAAO;IAIb,OAAO,CAAC,GAAG,EAAE,eAAe;CA+DnC"}
|
|
@@ -1,7 +1,67 @@
|
|
|
1
1
|
import { createFinding } from '../domain/finding.js';
|
|
2
2
|
import { findMatchingLine } from './match-evidence.js';
|
|
3
|
+
import { stripComments } from './strip-comments.js';
|
|
3
4
|
import { NETWORK_RULES } from './rules/index.js';
|
|
5
|
+
const HARDCODED_IP_RULE_ID = 'hardcoded-ip';
|
|
6
|
+
const IPV4_PATTERN = /\b(?:\d{1,3}\.){3}\d{1,3}\b/g;
|
|
4
7
|
const PRIVATE_IP_PATTERN = /\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3})\b/g;
|
|
8
|
+
const MAX_OCTET = 255;
|
|
9
|
+
const DOC_FILE_PATTERN = /(?:^|\/)(?:README|CHANGELOG|HISTORY|CONTRIBUTING|LICENSE|NOTICE)(?:\.\w+)?$|\.(?:md|mdx|markdown|rst|txt|adoc)$/i;
|
|
10
|
+
function isDocumentationFile(path) {
|
|
11
|
+
return DOC_FILE_PATTERN.test(path);
|
|
12
|
+
}
|
|
13
|
+
function parseOctets(ip) {
|
|
14
|
+
const octets = ip.split('.').map((part) => Number(part));
|
|
15
|
+
if (octets.length !== 4)
|
|
16
|
+
return undefined;
|
|
17
|
+
if (octets.some((n) => !Number.isInteger(n) || n < 0 || n > MAX_OCTET))
|
|
18
|
+
return undefined;
|
|
19
|
+
return octets;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A routable public IPv4 address - the only kind worth flagging as a hardcoded
|
|
23
|
+
* endpoint (potential C2 / exfil host). Loopback, private, link-local, CGNAT,
|
|
24
|
+
* documentation (RFC 5737), multicast, and reserved ranges are excluded: they
|
|
25
|
+
* are either benign examples or covered by the lower-severity private-ip rule.
|
|
26
|
+
*/
|
|
27
|
+
function isRoutablePublicIp(ip) {
|
|
28
|
+
const octets = parseOctets(ip);
|
|
29
|
+
if (!octets)
|
|
30
|
+
return false;
|
|
31
|
+
const [a, b, c] = octets;
|
|
32
|
+
if (a === 0 || a === 127)
|
|
33
|
+
return false;
|
|
34
|
+
if (a === 10)
|
|
35
|
+
return false;
|
|
36
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
37
|
+
return false;
|
|
38
|
+
if (a === 192 && b === 168)
|
|
39
|
+
return false;
|
|
40
|
+
if (a === 169 && b === 254)
|
|
41
|
+
return false;
|
|
42
|
+
if (a === 100 && b >= 64 && b <= 127)
|
|
43
|
+
return false;
|
|
44
|
+
if (a === 192 && b === 0 && c === 2)
|
|
45
|
+
return false;
|
|
46
|
+
if (a === 198 && b === 51 && c === 100)
|
|
47
|
+
return false;
|
|
48
|
+
if (a === 203 && b === 0 && c === 113)
|
|
49
|
+
return false;
|
|
50
|
+
if (a >= 224)
|
|
51
|
+
return false;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
function findRoutablePublicIp(content) {
|
|
55
|
+
IPV4_PATTERN.lastIndex = 0;
|
|
56
|
+
for (const match of content.matchAll(IPV4_PATTERN)) {
|
|
57
|
+
if (isRoutablePublicIp(match[0]))
|
|
58
|
+
return match[0];
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
function escapeRegExp(value) {
|
|
63
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
64
|
+
}
|
|
5
65
|
export class NetworkAnalyzer {
|
|
6
66
|
id = 'network';
|
|
7
67
|
supports() {
|
|
@@ -10,10 +70,13 @@ export class NetworkAnalyzer {
|
|
|
10
70
|
async analyze(ctx) {
|
|
11
71
|
const findings = [];
|
|
12
72
|
for (const file of ctx.artifact.files) {
|
|
73
|
+
const scanContent = stripComments(file.content, file.path);
|
|
13
74
|
for (const rule of NETWORK_RULES) {
|
|
75
|
+
if (rule.id === HARDCODED_IP_RULE_ID)
|
|
76
|
+
continue;
|
|
14
77
|
rule.pattern.lastIndex = 0;
|
|
15
|
-
if (rule.pattern.test(
|
|
16
|
-
const match = findMatchingLine(
|
|
78
|
+
if (rule.pattern.test(scanContent)) {
|
|
79
|
+
const match = findMatchingLine(scanContent, rule.pattern, file.content);
|
|
17
80
|
findings.push(createFinding({
|
|
18
81
|
category: 'network',
|
|
19
82
|
severity: rule.severity,
|
|
@@ -26,9 +89,27 @@ export class NetworkAnalyzer {
|
|
|
26
89
|
}));
|
|
27
90
|
}
|
|
28
91
|
}
|
|
92
|
+
const publicIp = findRoutablePublicIp(scanContent);
|
|
93
|
+
if (publicIp) {
|
|
94
|
+
const match = findMatchingLine(scanContent, new RegExp(escapeRegExp(publicIp)), file.content);
|
|
95
|
+
findings.push(createFinding({
|
|
96
|
+
category: 'network',
|
|
97
|
+
severity: 'MEDIUM',
|
|
98
|
+
title: 'Hardcoded IP',
|
|
99
|
+
description: `Hardcoded public IP address detected: ${publicIp}`,
|
|
100
|
+
ruleId: HARDCODED_IP_RULE_ID,
|
|
101
|
+
file: file.path,
|
|
102
|
+
line: match?.line,
|
|
103
|
+
evidence: match?.evidence,
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
// Private/loopback IPs are only a (low) signal in real code, never in docs
|
|
107
|
+
// or test fixtures where localhost examples are routine.
|
|
29
108
|
PRIVATE_IP_PATTERN.lastIndex = 0;
|
|
30
|
-
if (
|
|
31
|
-
|
|
109
|
+
if (!isDocumentationFile(file.path) &&
|
|
110
|
+
!file.path.includes('test') &&
|
|
111
|
+
PRIVATE_IP_PATTERN.test(scanContent)) {
|
|
112
|
+
const match = findMatchingLine(scanContent, PRIVATE_IP_PATTERN, file.content);
|
|
32
113
|
findings.push(createFinding({
|
|
33
114
|
category: 'network',
|
|
34
115
|
severity: 'LOW',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network-analyzer.js","sourceRoot":"","sources":["../../src/analyzers/network-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,kBAAkB,GAAG,8IAA8I,CAAC;
|
|
1
|
+
{"version":3,"file":"network-analyzer.js","sourceRoot":"","sources":["../../src/analyzers/network-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static-code-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzers/static-code-analyzer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"static-code-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzers/static-code-analyzer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAoB/D,qBAAa,kBAAmB,YAAW,QAAQ;IACjD,QAAQ,CAAC,EAAE,iBAAiB;IAE5B,QAAQ,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO;IAIjC,OAAO,CAAC,GAAG,EAAE,eAAe;CAqDnC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createFinding } from '../domain/finding.js';
|
|
2
2
|
import { findMatchingLine } from './match-evidence.js';
|
|
3
|
+
import { stripComments } from './strip-comments.js';
|
|
3
4
|
import { DANGEROUS_API_RULES, OBFUSCATION_RULES } from './rules/index.js';
|
|
4
5
|
const CODE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.py', '.go', '.rs', '.sh', '.bash', '.lua'];
|
|
5
6
|
function calculateEntropy(content) {
|
|
@@ -23,10 +24,11 @@ export class StaticCodeAnalyzer {
|
|
|
23
24
|
const findings = [];
|
|
24
25
|
const codeFiles = ctx.artifact.files.filter((f) => CODE_EXTENSIONS.some((ext) => f.path.endsWith(ext)));
|
|
25
26
|
for (const file of codeFiles) {
|
|
27
|
+
const codeOnly = stripComments(file.content, file.path);
|
|
26
28
|
for (const rule of [...DANGEROUS_API_RULES, ...OBFUSCATION_RULES]) {
|
|
27
29
|
rule.pattern.lastIndex = 0;
|
|
28
|
-
if (rule.pattern.test(
|
|
29
|
-
const match = findMatchingLine(
|
|
30
|
+
if (rule.pattern.test(codeOnly)) {
|
|
31
|
+
const match = findMatchingLine(codeOnly, rule.pattern, file.content);
|
|
30
32
|
findings.push(createFinding({
|
|
31
33
|
category: 'static-code',
|
|
32
34
|
severity: rule.severity,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static-code-analyzer.js","sourceRoot":"","sources":["../../src/analyzers/static-code-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1E,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAEpH,SAAS,gBAAgB,CAAC,OAAe;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QAClC,MAAM,CAAC,GAAG,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC;QACjC,OAAO,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,OAAO,kBAAkB;IACpB,EAAE,GAAG,aAAa,CAAC;IAE5B,QAAQ,CAAC,GAAoB;QAC3B,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7F,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAoB;QAChC,MAAM,QAAQ,GAAG,EAAE,CAAC;QACpB,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAChD,eAAe,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CACpD,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,mBAAmB,EAAE,GAAG,iBAAiB,CAAC,EAAE,CAAC;gBAClE,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;gBAC3B,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"static-code-analyzer.js","sourceRoot":"","sources":["../../src/analyzers/static-code-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replaces comment content with spaces while preserving newlines, string
|
|
3
|
+
* literals, and overall character positions. Line numbers and column offsets
|
|
4
|
+
* stay intact so findings still map to the right source location.
|
|
5
|
+
*
|
|
6
|
+
* String-aware: a `//` inside "http://..." is NOT treated as a comment.
|
|
7
|
+
*/
|
|
8
|
+
export declare function stripComments(content: string, path: string): string;
|
|
9
|
+
//# sourceMappingURL=strip-comments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip-comments.d.ts","sourceRoot":"","sources":["../../src/analyzers/strip-comments.ts"],"names":[],"mappings":"AAcA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAmEnE"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const C_STYLE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.go', '.rs'];
|
|
2
|
+
const HASH_STYLE_EXTENSIONS = ['.py', '.sh', '.bash', '.zsh'];
|
|
3
|
+
const LUA_STYLE_EXTENSIONS = ['.lua'];
|
|
4
|
+
function commentStyleFor(path) {
|
|
5
|
+
const lower = path.toLowerCase();
|
|
6
|
+
if (C_STYLE_EXTENSIONS.some((ext) => lower.endsWith(ext)))
|
|
7
|
+
return 'c';
|
|
8
|
+
if (HASH_STYLE_EXTENSIONS.some((ext) => lower.endsWith(ext)))
|
|
9
|
+
return 'hash';
|
|
10
|
+
if (LUA_STYLE_EXTENSIONS.some((ext) => lower.endsWith(ext)))
|
|
11
|
+
return 'lua';
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Replaces comment content with spaces while preserving newlines, string
|
|
16
|
+
* literals, and overall character positions. Line numbers and column offsets
|
|
17
|
+
* stay intact so findings still map to the right source location.
|
|
18
|
+
*
|
|
19
|
+
* String-aware: a `//` inside "http://..." is NOT treated as a comment.
|
|
20
|
+
*/
|
|
21
|
+
export function stripComments(content, path) {
|
|
22
|
+
const style = commentStyleFor(path);
|
|
23
|
+
if (!style)
|
|
24
|
+
return content;
|
|
25
|
+
const out = [];
|
|
26
|
+
let i = 0;
|
|
27
|
+
const n = content.length;
|
|
28
|
+
const isLineComment = (idx) => {
|
|
29
|
+
if (style === 'c' && content[idx] === '/' && content[idx + 1] === '/')
|
|
30
|
+
return 2;
|
|
31
|
+
if (style === 'hash' && content[idx] === '#')
|
|
32
|
+
return 1;
|
|
33
|
+
if (style === 'lua' && content[idx] === '-' && content[idx + 1] === '-')
|
|
34
|
+
return 2;
|
|
35
|
+
return 0;
|
|
36
|
+
};
|
|
37
|
+
while (i < n) {
|
|
38
|
+
const ch = content[i];
|
|
39
|
+
// String literals - copy verbatim, respecting escapes.
|
|
40
|
+
if (ch === '"' || ch === "'" || (style === 'c' && ch === '`')) {
|
|
41
|
+
const quote = ch;
|
|
42
|
+
out.push(ch);
|
|
43
|
+
i++;
|
|
44
|
+
while (i < n) {
|
|
45
|
+
const c = content[i];
|
|
46
|
+
out.push(c === '\n' ? '\n' : c);
|
|
47
|
+
if (c === '\\') {
|
|
48
|
+
if (i + 1 < n) {
|
|
49
|
+
out.push(content[i + 1] === '\n' ? '\n' : content[i + 1]);
|
|
50
|
+
i += 2;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
i++;
|
|
55
|
+
if (c === quote)
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// C-style block comment.
|
|
61
|
+
if (style === 'c' && ch === '/' && content[i + 1] === '*') {
|
|
62
|
+
while (i < n && !(content[i] === '*' && content[i + 1] === '/')) {
|
|
63
|
+
out.push(content[i] === '\n' ? '\n' : ' ');
|
|
64
|
+
i++;
|
|
65
|
+
}
|
|
66
|
+
if (i < n) {
|
|
67
|
+
out.push(' ');
|
|
68
|
+
i += 2;
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
// Line comment - blank out to end of line.
|
|
73
|
+
const lineCommentLen = isLineComment(i);
|
|
74
|
+
if (lineCommentLen > 0) {
|
|
75
|
+
while (i < n && content[i] !== '\n') {
|
|
76
|
+
out.push(' ');
|
|
77
|
+
i++;
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
out.push(ch);
|
|
82
|
+
i++;
|
|
83
|
+
}
|
|
84
|
+
return out.join('');
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=strip-comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip-comments.js","sourceRoot":"","sources":["../../src/analyzers/strip-comments.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const TEST_TOOL_PACKAGES: Set<string>;
|
|
1
|
+
import type { Finding } from '../domain/finding.js';
|
|
3
2
|
export declare function isTestPath(filePath: string): boolean;
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Whether a finding is worth surfacing when it lives in a test/fixture file.
|
|
5
|
+
* Leaked secrets and explicit malware signatures qualify; benign dev-pattern
|
|
6
|
+
* findings (shell APIs, dynamic imports, etc.) do not.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isTestRelevantFinding(finding: Finding): boolean;
|
|
5
9
|
//# sourceMappingURL=test-path.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-path.d.ts","sourceRoot":"","sources":["../../src/analyzers/test-path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"test-path.d.ts","sourceRoot":"","sources":["../../src/analyzers/test-path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AA4DpD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAYpD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAI/D"}
|
|
@@ -15,26 +15,35 @@ const TEST_DIR_SEGMENTS = new Set([
|
|
|
15
15
|
'test-runner',
|
|
16
16
|
'testing',
|
|
17
17
|
]);
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
18
|
+
/**
|
|
19
|
+
* Rule IDs that stay meaningful inside test/fixture code. Everyday dev patterns
|
|
20
|
+
* (child_process, spawn, eval, dynamic import, ...) are expected in tests and
|
|
21
|
+
* are NOT flagged there - but malware loves to hide in fixtures, so we keep the
|
|
22
|
+
* unambiguous attack signatures: remote payload delivery, miners, packed
|
|
23
|
+
* binaries, exfiltration/C2 channels, and prompt-injection attacks.
|
|
24
|
+
*/
|
|
25
|
+
const TEST_MALWARE_RULE_IDS = new Set([
|
|
26
|
+
'curl-pipe',
|
|
27
|
+
'wget-pipe',
|
|
28
|
+
'crypto-miner',
|
|
29
|
+
'packed-upx',
|
|
30
|
+
'discord-webhook',
|
|
31
|
+
'telegram-bot',
|
|
32
|
+
'pastebin',
|
|
33
|
+
'ngrok',
|
|
34
|
+
'cloudflare-tunnel',
|
|
35
|
+
'tor',
|
|
36
|
+
'dynamic-dns',
|
|
37
|
+
'ignore-instructions',
|
|
38
|
+
'system-prompt-extract',
|
|
39
|
+
'tool-escalation',
|
|
40
|
+
'memory-poison',
|
|
41
|
+
'self-update',
|
|
42
|
+
'hidden-goal',
|
|
43
|
+
'jailbreak',
|
|
44
|
+
'recursive-agent',
|
|
45
|
+
'fake-success',
|
|
46
|
+
'activation-phrase',
|
|
38
47
|
]);
|
|
39
48
|
function isTestFileName(name) {
|
|
40
49
|
const lower = name.toLowerCase();
|
|
@@ -53,10 +62,16 @@ export function isTestPath(filePath) {
|
|
|
53
62
|
.slice(0, -1)
|
|
54
63
|
.some((segment) => TEST_DIR_SEGMENTS.has(segment.toLowerCase()));
|
|
55
64
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Whether a finding is worth surfacing when it lives in a test/fixture file.
|
|
67
|
+
* Leaked secrets and explicit malware signatures qualify; benign dev-pattern
|
|
68
|
+
* findings (shell APIs, dynamic imports, etc.) do not.
|
|
69
|
+
*/
|
|
70
|
+
export function isTestRelevantFinding(finding) {
|
|
71
|
+
if (finding.positive)
|
|
72
|
+
return true;
|
|
73
|
+
if (finding.category === 'secret')
|
|
74
|
+
return true;
|
|
75
|
+
return finding.ruleId !== undefined && TEST_MALWARE_RULE_IDS.has(finding.ruleId);
|
|
61
76
|
}
|
|
62
77
|
//# sourceMappingURL=test-path.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-path.js","sourceRoot":"","sources":["../../src/analyzers/test-path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"test-path.js","sourceRoot":"","sources":["../../src/analyzers/test-path.ts"],"names":[],"mappings":"AAEA,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IAChC,MAAM;IACN,OAAO;IACP,WAAW;IACX,WAAW;IACX,cAAc;IACd,UAAU;IACV,SAAS;IACT,MAAM;IACN,OAAO;IACP,KAAK;IACL,OAAO;IACP,UAAU;IACV,aAAa;IACb,aAAa;IACb,SAAS;CACV,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,WAAW;IACX,WAAW;IACX,cAAc;IACd,YAAY;IACZ,iBAAiB;IACjB,cAAc;IACd,UAAU;IACV,OAAO;IACP,mBAAmB;IACnB,KAAK;IACL,aAAa;IACb,qBAAqB;IACrB,uBAAuB;IACvB,iBAAiB;IACjB,eAAe;IACf,aAAa;IACb,aAAa;IACb,WAAW;IACX,iBAAiB;IACjB,cAAc;IACd,mBAAmB;CACpB,CAAC,CAAC;AAEH,SAAS,cAAc,CAAC,IAAY;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,OAAO,CACL,gCAAgC,CAAC,IAAI,CAAC,KAAK,CAAC;QAC5C,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC;QAC/B,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAC5B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAErD,IAAI,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,QAAQ;SACZ,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;SACZ,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AACrE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,IAAI,OAAO,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AACnF,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -66,15 +66,16 @@ program
|
|
|
66
66
|
.option('--json', 'Output the full verification report as JSON (machine-readable)')
|
|
67
67
|
.option('--markdown', 'Output the report as Markdown')
|
|
68
68
|
.option('--policy <file>', 'Path to a JSON policy file (overrides default thresholds and lists)')
|
|
69
|
-
.option('--score-tests', '
|
|
69
|
+
.option('--score-tests', 'Scan test/fixture files with the full ruleset (default: secrets and malware only)')
|
|
70
70
|
.addHelpText('after', `${VERIFY_EXAMPLES}
|
|
71
71
|
Options (default output is a colored terminal report):
|
|
72
72
|
--json Structured JSON with findings, risk, policy, permissions, evidence
|
|
73
73
|
--markdown Markdown table suitable for docs or PR comments
|
|
74
74
|
--policy Custom policy: blockThreshold, warnThreshold, minConfidence,
|
|
75
75
|
trustedPublishers, corporateWhitelist, corporateBlacklist, etc.
|
|
76
|
-
--score-tests
|
|
77
|
-
|
|
76
|
+
--score-tests Scan test/fixture files with the full production ruleset.
|
|
77
|
+
By default tests are only checked for leaked secrets and
|
|
78
|
+
malware signatures (curl|bash, miners, exfiltration, etc.)
|
|
78
79
|
|
|
79
80
|
${TARGET_TYPES_HELP}`)
|
|
80
81
|
.action(async (type, target, opts) => {
|
|
@@ -109,13 +110,14 @@ program
|
|
|
109
110
|
.option('--json', 'Output the verification report as JSON instead of terminal format')
|
|
110
111
|
.option('--yes', 'Auto-approve installation after verification (cannot override BLOCK)')
|
|
111
112
|
.option('--policy <file>', 'Path to a JSON policy file (overrides default thresholds and lists)')
|
|
112
|
-
.option('--score-tests', '
|
|
113
|
+
.option('--score-tests', 'Scan test/fixture files with the full ruleset (default: secrets and malware only)')
|
|
113
114
|
.addHelpText('after', `${INSTALL_EXAMPLES}
|
|
114
115
|
Options:
|
|
115
116
|
--json Output report as JSON (terminal format is default)
|
|
116
117
|
--yes Skip the interactive [y/N] approval prompt when policy allows it
|
|
117
118
|
--policy Custom policy file (same fields as verify --policy)
|
|
118
|
-
--score-tests
|
|
119
|
+
--score-tests Scan test/fixture files with the full production ruleset
|
|
120
|
+
(default: tests checked only for secrets and malware)
|
|
119
121
|
|
|
120
122
|
Install behavior by ecosystem:
|
|
121
123
|
npm npm install <name> --ignore-scripts
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,MAAM,iBAAiB,GAAG;;;;;;;;;;;CAWzB,CAAC;AAEF,MAAM,eAAe,GAAG;;;;;;;;;;CAUvB,CAAC;AAEF,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;CAaxB,CAAC;AAEF,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,sEAAsE,CAAC;KACnF,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,OAAO,EAAE;;;;;;;;;;;EAWtB,iBAAiB,EAAE,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,yEAAyE,CAAC;KACtF,QAAQ,CAAC,QAAQ,EAAE,gDAAgD,CAAC;KACpE,QAAQ,CAAC,UAAU,EAAE,8CAA8C,CAAC;KACpE,MAAM,CAAC,QAAQ,EAAE,gEAAgE,CAAC;KAClF,MAAM,CAAC,YAAY,EAAE,+BAA+B,CAAC;KACrD,MAAM,CAAC,iBAAiB,EAAE,qEAAqE,CAAC;KAChG,MAAM,CAAC,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
package/dist/core/sentinel.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export declare class Sentinel {
|
|
|
24
24
|
install(ecosystem: string, raw: string, options?: {
|
|
25
25
|
forceApprove?: boolean;
|
|
26
26
|
onVerified?: () => void;
|
|
27
|
+
cwd?: string;
|
|
27
28
|
}): Promise<InstallResult>;
|
|
28
29
|
generateReport(report: VerificationReport, format?: ReportFormat): string;
|
|
29
30
|
private runInstaller;
|