mcp-security-lab 0.2.0
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/LICENSE +17 -0
- package/README.md +175 -0
- package/SECURITY.md +25 -0
- package/THREAT_MODEL.md +66 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +113 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.js +90 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/environment.d.ts +12 -0
- package/dist/src/environment.js +145 -0
- package/dist/src/environment.js.map +1 -0
- package/dist/src/reporter.d.ts +4 -0
- package/dist/src/reporter.js +161 -0
- package/dist/src/reporter.js.map +1 -0
- package/dist/src/rules/injection.d.ts +16 -0
- package/dist/src/rules/injection.js +91 -0
- package/dist/src/rules/injection.js.map +1 -0
- package/dist/src/rules/launch.d.ts +2 -0
- package/dist/src/rules/launch.js +102 -0
- package/dist/src/rules/launch.js.map +1 -0
- package/dist/src/rules/oauth.d.ts +21 -0
- package/dist/src/rules/oauth.js +297 -0
- package/dist/src/rules/oauth.js.map +1 -0
- package/dist/src/rules/remote.d.ts +12 -0
- package/dist/src/rules/remote.js +81 -0
- package/dist/src/rules/remote.js.map +1 -0
- package/dist/src/rules/tools.d.ts +9 -0
- package/dist/src/rules/tools.js +216 -0
- package/dist/src/rules/tools.js.map +1 -0
- package/dist/src/scanner.d.ts +2 -0
- package/dist/src/scanner.js +393 -0
- package/dist/src/scanner.js.map +1 -0
- package/dist/src/types.d.ts +72 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/docs/RULES.md +108 -0
- package/package.json +93 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 MCP Security Lab contributors
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# MCP Security Lab
|
|
2
|
+
|
|
3
|
+
[](https://github.com/warlyjr-cloud/mcp-security-lab/actions/workflows/ci.yml)
|
|
4
|
+
[](https://badge.fury.io/js/mcp-security-lab)
|
|
5
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
6
|
+
|
|
7
|
+
Evidence-first security checks for local and remote Model Context Protocol (MCP) servers.
|
|
8
|
+
|
|
9
|
+
> **MCP Security Lab is a DevSecOps DevTool that automatically audits Model Context Protocol (MCP) servers for prompt injections, context window exhaustion, and unsafe tool schemas without requiring an LLM or API keys. It runs natively in CI/CD pipelines (SARIF 2.1.0) and supports Docker Sandboxing for safe active probing.**
|
|
10
|
+
|
|
11
|
+
The MVP audits how a local or remote MCP server is launched and what it advertises during the MCP
|
|
12
|
+
handshake. It detects risky launcher patterns (including inline code execution and encoded payloads),
|
|
13
|
+
missing tool annotations, mixed read/write interfaces, least-privilege violations, overly broad
|
|
14
|
+
schemas, context exhaustion risks, and prompt-injection-like text. Prompt-injection scanning covers
|
|
15
|
+
the full surface a server injects into the model's context — tool descriptions, annotation titles,
|
|
16
|
+
parameter names, enum values, server instructions, and every advertised **prompt** and **resource** —
|
|
17
|
+
and is resilient to Unicode evasion (homoglyphs, zero-width and bidirectional characters).
|
|
18
|
+
|
|
19
|
+
Every finding is mapped to a **CWE** and, where relevant, to the **OWASP Top 10 for LLM Applications**,
|
|
20
|
+
and this taxonomy is emitted in the SARIF output. By default the scanner does not call discovered
|
|
21
|
+
tools and requires no LLM or API key. Reports can be emitted as text, JSON, or SARIF 2.1.0. The full
|
|
22
|
+
rule catalog is documented in [docs/RULES.md](docs/RULES.md); the trust boundaries are described in
|
|
23
|
+
[THREAT_MODEL.md](THREAT_MODEL.md).
|
|
24
|
+
|
|
25
|
+
## Why this exists
|
|
26
|
+
|
|
27
|
+
Installing a local MCP server executes code with the user's privileges. Protocol compliance
|
|
28
|
+
alone does not show whether a server asks for excessive access or advertises unsafe tools.
|
|
29
|
+
MCP Security Lab produces a small, reviewable report before deeper testing.
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
npm.cmd install
|
|
35
|
+
npm.cmd run build
|
|
36
|
+
node dist/src/cli.js scan --config examples/insecure-server.json
|
|
37
|
+
node dist/src/cli.js scan --config examples/insecure-server.json --execute
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The first scan performs launch-configuration checks only. `--execute` acknowledges that the
|
|
41
|
+
target command will run and enables MCP discovery.
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"target": {
|
|
48
|
+
"command": "node",
|
|
49
|
+
"args": ["dist/fixtures/insecure-server.js"],
|
|
50
|
+
"cwd": "."
|
|
51
|
+
},
|
|
52
|
+
"policy": {
|
|
53
|
+
"timeoutMs": 5000,
|
|
54
|
+
"maxTools": 100
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Paths are resolved relative to the configuration file. Environment values are intentionally
|
|
60
|
+
not supported in the MVP; this prevents secrets from entering reports or committed examples.
|
|
61
|
+
|
|
62
|
+
## Output
|
|
63
|
+
|
|
64
|
+
```powershell
|
|
65
|
+
node dist/src/cli.js scan --config examples/insecure-server.json --execute --format json
|
|
66
|
+
node dist/src/cli.js scan --config examples/insecure-server.json --execute --format sarif
|
|
67
|
+
node dist/src/cli.js scan --config examples/insecure-server.json --execute --output reports/scan.json
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Sensitive values supplied through common arguments such as `--token`, `--api-key`,
|
|
71
|
+
`--password`, and `--secret` are redacted from every report. Avoid credentials in command
|
|
72
|
+
arguments entirely when the target supports a safer authentication mechanism.
|
|
73
|
+
|
|
74
|
+
Exit codes:
|
|
75
|
+
|
|
76
|
+
- `0`: scan completed without high or critical findings
|
|
77
|
+
- `1`: invalid input or runtime failure
|
|
78
|
+
- `2`: scan completed with at least one high or critical finding
|
|
79
|
+
|
|
80
|
+
## Remote servers
|
|
81
|
+
|
|
82
|
+
To scan a remote MCP server, give the target a `url` instead of a `command`:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"target": {
|
|
87
|
+
"url": "https://mcp.example.com/mcp",
|
|
88
|
+
"transport": "http"
|
|
89
|
+
},
|
|
90
|
+
"policy": { "timeoutMs": 5000, "maxTools": 100 }
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`transport` defaults to `"http"` (the modern **Streamable HTTP** transport); use `"sse"` only for
|
|
95
|
+
legacy servers. Remote targets are checked for plaintext HTTP on non-local hosts (`REMOTE002`),
|
|
96
|
+
credentials embedded in the URL (`REMOTE003`, redacted from the report), and — with `--execute` —
|
|
97
|
+
whether the server accepts unauthenticated connections (`REMOTE004`).
|
|
98
|
+
|
|
99
|
+
With `--execute`, a protected remote server is also audited against the MCP authorization spec
|
|
100
|
+
(OAuth 2.1 + RFC 9728 / RFC 8414): a missing resource-metadata pointer (`AUTH001`), unreachable or
|
|
101
|
+
malformed OAuth metadata (`AUTH002`), an authorization server that does not require PKCE/S256
|
|
102
|
+
(`AUTH003`), and OAuth endpoints served over plaintext HTTP (`AUTH004`). See [docs/RULES.md](docs/RULES.md).
|
|
103
|
+
|
|
104
|
+
```powershell
|
|
105
|
+
node dist/src/cli.js scan --config examples/remote-server.json --execute
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Active probing (fuzzing)
|
|
109
|
+
|
|
110
|
+
By default the scanner never invokes a discovered tool. Active probing is opt-in through `--fuzz`,
|
|
111
|
+
which sends malformed and injection-shaped arguments to each tool to see whether the server validates
|
|
112
|
+
input or crashes/hangs. Because this executes tool code, `--fuzz` **requires both `--execute` and
|
|
113
|
+
`--sandbox docker`** so the target is network-isolated; the scanner refuses to fuzz on the host.
|
|
114
|
+
|
|
115
|
+
```powershell
|
|
116
|
+
node dist/src/cli.js scan --config examples/vulnerable-server.json --execute --sandbox docker --fuzz
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`examples/vulnerable-server.json` targets a bundled honeypot (`fixtures/honeypot.ts`) that intentionally
|
|
120
|
+
crashes on malicious input, so this command demonstrates a `FUZZ001` critical finding. A well-behaved
|
|
121
|
+
server that returns a proper MCP error for bad input produces no fuzzing finding.
|
|
122
|
+
|
|
123
|
+
## GitHub Action
|
|
124
|
+
|
|
125
|
+
The repository includes a composite action that generates SARIF and can upload it to GitHub
|
|
126
|
+
code scanning. The calling workflow must grant `security-events: write`.
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
name: MCP security
|
|
130
|
+
|
|
131
|
+
on:
|
|
132
|
+
workflow_dispatch:
|
|
133
|
+
|
|
134
|
+
permissions:
|
|
135
|
+
contents: read
|
|
136
|
+
security-events: write
|
|
137
|
+
|
|
138
|
+
jobs:
|
|
139
|
+
scan:
|
|
140
|
+
runs-on: ubuntu-latest
|
|
141
|
+
steps:
|
|
142
|
+
- uses: actions/checkout@v6
|
|
143
|
+
- uses: warlyjr-cloud/mcp-security-lab@v0.2.0
|
|
144
|
+
with:
|
|
145
|
+
config: mcp-security-lab.json
|
|
146
|
+
execute: "true"
|
|
147
|
+
upload-sarif: "true"
|
|
148
|
+
fail-on-findings: "true"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Use `execute: "false"` for launch-configuration checks that must not start the target.
|
|
152
|
+
SARIF upload is available for public repositories and for eligible private repositories with
|
|
153
|
+
GitHub Code Security enabled.
|
|
154
|
+
|
|
155
|
+
`execute: "true"` runs the configured server without OS-level filesystem or network isolation.
|
|
156
|
+
Use it only for reviewed code or inside a disposable runner that contains no sensitive source
|
|
157
|
+
or credentials.
|
|
158
|
+
|
|
159
|
+
## Current limitations
|
|
160
|
+
|
|
161
|
+
Without `--sandbox docker`, `--execute` provides no OS-level filesystem or network isolation: the
|
|
162
|
+
server runs with the current user's permissions. Use `--sandbox docker` (which runs the target in a
|
|
163
|
+
`--network none` container) or a disposable VM for untrusted software. The Docker adapter is
|
|
164
|
+
POSIX-oriented; Windows paths are translated for bind-mounts but Docker Desktop must be running.
|
|
165
|
+
Remote scanning uses the Streamable HTTP transport by default (SSE is available for legacy servers).
|
|
166
|
+
See [SECURITY.md](SECURITY.md) and [THREAT_MODEL.md](THREAT_MODEL.md).
|
|
167
|
+
|
|
168
|
+
## Roadmap
|
|
169
|
+
|
|
170
|
+
1. Windows Sandbox execution adapter (Docker container adapter shipped).
|
|
171
|
+
2. Explicit, synthetic tool-call scenarios with canary files and blocked egress.
|
|
172
|
+
3. Public conformance corpus maintained with the MCP community.
|
|
173
|
+
|
|
174
|
+
Shipped: Streamable HTTP transport, remote transport checks, and OAuth authorization-posture checks
|
|
175
|
+
(RFC 9728 / RFC 8414).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Security model
|
|
2
|
+
|
|
3
|
+
MCP Security Lab treats a configured MCP server as untrusted code.
|
|
4
|
+
|
|
5
|
+
## What the MVP protects
|
|
6
|
+
|
|
7
|
+
- Dynamic scanning requires the explicit `--execute` flag.
|
|
8
|
+
- The target receives a small allowlist of inherited environment variables.
|
|
9
|
+
- Target startup and protocol discovery have a hard timeout.
|
|
10
|
+
- The scanner lists tools but never calls them.
|
|
11
|
+
- Findings are deterministic and contain no environment values.
|
|
12
|
+
|
|
13
|
+
## What the MVP does not protect
|
|
14
|
+
|
|
15
|
+
The current process runner is not an OS-level sandbox. A target process still runs with the
|
|
16
|
+
current user's operating-system permissions and may access the filesystem or network. Run
|
|
17
|
+
unknown servers inside a disposable VM or container.
|
|
18
|
+
|
|
19
|
+
Future releases may add platform adapters for Windows Sandbox, containers, and restricted
|
|
20
|
+
egress. Those adapters must be opt-in and independently verifiable.
|
|
21
|
+
|
|
22
|
+
## Reporting vulnerabilities
|
|
23
|
+
|
|
24
|
+
Do not include credentials, private server configuration, or personal data in a public issue.
|
|
25
|
+
Provide a minimal synthetic reproduction and identify the affected version.
|
package/THREAT_MODEL.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Threat model
|
|
2
|
+
|
|
3
|
+
This document states what MCP Security Lab treats as trusted, what it treats as hostile, and what
|
|
4
|
+
it does and does not guarantee. It complements [SECURITY.md](SECURITY.md), which covers vulnerability
|
|
5
|
+
reporting.
|
|
6
|
+
|
|
7
|
+
## Assets we protect
|
|
8
|
+
|
|
9
|
+
- The operator's workstation or CI runner (filesystem, network, credentials, environment).
|
|
10
|
+
- The integrity of the scan report itself (no secret leakage, no attacker-controlled content that
|
|
11
|
+
can crash or mislead the scanner).
|
|
12
|
+
|
|
13
|
+
## Trust boundaries
|
|
14
|
+
|
|
15
|
+
| Component | Trust level | Rationale |
|
|
16
|
+
| --------------------------------- | ----------- | ------------------------------------------------------ |
|
|
17
|
+
| The scanner code and its config | Trusted | Authored and reviewed by the operator. |
|
|
18
|
+
| The target MCP server binary/args | Untrusted | May be third-party or malicious; running it is a risk. |
|
|
19
|
+
| Everything the server sends | Untrusted | Tool/prompt/resource metadata is attacker-controlled. |
|
|
20
|
+
| The parent process environment | Sensitive | Must not leak into the target or the report. |
|
|
21
|
+
|
|
22
|
+
Everything a target server returns — names, descriptions, schemas, annotations, instructions, prompt
|
|
23
|
+
and resource metadata — is untrusted input and is handled defensively. A target must never be able to
|
|
24
|
+
crash the scanner, exfiltrate the operator's environment, or inject content into the report.
|
|
25
|
+
|
|
26
|
+
## Adversaries and the mitigations against them
|
|
27
|
+
|
|
28
|
+
- **Malicious launcher (supply chain).** A config that runs a shell, a network installer
|
|
29
|
+
(`npx`/`uvx`), inline interpreter code (`node -e`, `python -c`), or encoded payloads. Detected
|
|
30
|
+
statically without execution (`LAUNCH001`–`LAUNCH004`).
|
|
31
|
+
- **Prompt injection via advertised metadata.** Hidden instructions in tool descriptions, parameter
|
|
32
|
+
names, enum values, server instructions, prompts, or resources — including Unicode-obfuscated
|
|
33
|
+
variants. Detected after NFKC normalization and invisible-character stripping (`TOOL003`,
|
|
34
|
+
`TOOL012`).
|
|
35
|
+
- **Excessive agency / least-privilege violations.** Undeclared destructive tools, missing safety
|
|
36
|
+
hints, read+write capability mixing (`TOOL005`–`TOOL008`, `TOOL011`).
|
|
37
|
+
- **Context-exhaustion / denial of service.** Unbounded read tools, tool flooding, or oversized
|
|
38
|
+
discovery payloads (`TOOL010`, `DISC001`, `DISC002`). The scanner bounds process lifetime with a
|
|
39
|
+
timeout and caps the size of discovery responses so a hostile server cannot exhaust the scanner.
|
|
40
|
+
- **Insecure remote transport.** Plaintext HTTP on non-local endpoints, credentials in the URL, or a
|
|
41
|
+
remote server that accepts anonymous connections (`REMOTE002`–`REMOTE004`).
|
|
42
|
+
- **Weak OAuth authorization posture.** A protected remote server that omits its resource-metadata
|
|
43
|
+
pointer, serves unreachable/malformed OAuth metadata, does not require PKCE, or exposes OAuth
|
|
44
|
+
endpoints over plaintext HTTP (`AUTH001`–`AUTH004`, per RFC 9728 / RFC 8414).
|
|
45
|
+
- **Secret leakage into reports.** Credentials passed as arguments or in URLs are redacted; only an
|
|
46
|
+
allowlist of environment variables is inherited by the target.
|
|
47
|
+
- **Uncontrolled active probing.** `--fuzz` executes tool code, so it requires an OS-level sandbox
|
|
48
|
+
(`--sandbox docker`, `--network none`) and refuses to run on the host.
|
|
49
|
+
|
|
50
|
+
## Explicit non-goals
|
|
51
|
+
|
|
52
|
+
- **No isolation without `--sandbox docker`.** A target started with `--execute` alone runs with the
|
|
53
|
+
operator's OS permissions. The scanner never claims isolation it does not enforce.
|
|
54
|
+
- **No dynamic tool invocation by default.** Tools are only called when `--fuzz` is explicitly
|
|
55
|
+
requested and a sandbox is active.
|
|
56
|
+
- **Not a substitute for code review.** Static launcher checks and advertised-metadata analysis
|
|
57
|
+
reduce risk; they do not prove a server is safe.
|
|
58
|
+
- **No guarantee against a determined sandbox escape.** Container isolation reduces, but does not
|
|
59
|
+
eliminate, the risk of running untrusted code. Use disposable runners for unknown software.
|
|
60
|
+
- **OAuth discovery follows server-controlled URLs (SSRF).** Auditing a remote server's authorization
|
|
61
|
+
posture (`AUTH00x`) requires fetching the metadata URLs the server advertises. Before contacting any
|
|
62
|
+
such URL the scanner resolves the host and refuses loopback, link-local (incl. `169.254.169.254`),
|
|
63
|
+
and private addresses — reporting the attempt as `AUTH005` — requires https for non-local hosts, uses
|
|
64
|
+
`redirect: "manual"` so a 3xx cannot bypass the check, caps the number of authorization servers
|
|
65
|
+
contacted, and bounds every request with a timeout. Loopback metadata is followed only when the
|
|
66
|
+
operator's own target is local (a dev scan). The scanner never forwards responses anywhere.
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import { loadConfig } from "./config.js";
|
|
5
|
+
import { reportAsJson, reportAsSarif, reportAsText } from "./reporter.js";
|
|
6
|
+
import { scan } from "./scanner.js";
|
|
7
|
+
function usage() {
|
|
8
|
+
return [
|
|
9
|
+
"Usage:",
|
|
10
|
+
" mcpsl scan --config <path> [--execute] [--sandbox docker|none] [--fuzz]",
|
|
11
|
+
" [--format text|json|sarif] [--output <path>]",
|
|
12
|
+
"",
|
|
13
|
+
"Safety:",
|
|
14
|
+
" Without --execute, only the launch configuration is inspected.",
|
|
15
|
+
" With --execute, the target starts with a filtered environment; its tools are never called.",
|
|
16
|
+
" Use --sandbox docker to run the target inside a disposable Docker container.",
|
|
17
|
+
" Use --fuzz to actively call discovered tools with malicious inputs (DANGEROUS).",
|
|
18
|
+
" --fuzz requires --execute and --sandbox docker so probing stays isolated.",
|
|
19
|
+
].join("\n");
|
|
20
|
+
}
|
|
21
|
+
function parseArgs(args) {
|
|
22
|
+
if (args[0] !== "scan") {
|
|
23
|
+
throw new Error(usage());
|
|
24
|
+
}
|
|
25
|
+
let configPath;
|
|
26
|
+
let execute = false;
|
|
27
|
+
let fuzz = false;
|
|
28
|
+
let format = "text";
|
|
29
|
+
let outputPath;
|
|
30
|
+
let sandbox = "none";
|
|
31
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
32
|
+
const argument = args[index];
|
|
33
|
+
if (argument === "--execute") {
|
|
34
|
+
execute = true;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (argument === "--fuzz") {
|
|
38
|
+
fuzz = true;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (argument === "--config" ||
|
|
42
|
+
argument === "--format" ||
|
|
43
|
+
argument === "--output" ||
|
|
44
|
+
argument === "--sandbox") {
|
|
45
|
+
const value = args[index + 1];
|
|
46
|
+
if (value === undefined) {
|
|
47
|
+
throw new Error(`${argument} requires a value.`);
|
|
48
|
+
}
|
|
49
|
+
index += 1;
|
|
50
|
+
if (argument === "--config") {
|
|
51
|
+
configPath = value;
|
|
52
|
+
}
|
|
53
|
+
else if (argument === "--output") {
|
|
54
|
+
outputPath = value;
|
|
55
|
+
}
|
|
56
|
+
else if (argument === "--sandbox") {
|
|
57
|
+
if (value === "docker" || value === "none") {
|
|
58
|
+
sandbox = value;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
throw new Error("--sandbox must be docker or none.");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else if (value === "text" || value === "json" || value === "sarif") {
|
|
65
|
+
format = value;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
throw new Error("--format must be text, json, or sarif.");
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
throw new Error(`Unknown argument: ${argument}\n\n${usage()}`);
|
|
73
|
+
}
|
|
74
|
+
if (configPath === undefined) {
|
|
75
|
+
throw new Error(`--config is required.\n\n${usage()}`);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
configPath,
|
|
79
|
+
execute,
|
|
80
|
+
fuzz,
|
|
81
|
+
format,
|
|
82
|
+
sandbox,
|
|
83
|
+
...(outputPath === undefined ? {} : { outputPath }),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
async function main() {
|
|
87
|
+
const options = parseArgs(process.argv.slice(2));
|
|
88
|
+
const config = await loadConfig(options.configPath);
|
|
89
|
+
const report = await scan(config, options.execute, options.sandbox, options.fuzz);
|
|
90
|
+
const output = options.format === "json"
|
|
91
|
+
? reportAsJson(report)
|
|
92
|
+
: options.format === "sarif"
|
|
93
|
+
? reportAsSarif(report, options.configPath)
|
|
94
|
+
: reportAsText(report);
|
|
95
|
+
if (options.outputPath === undefined) {
|
|
96
|
+
process.stdout.write(output);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const absoluteOutputPath = resolve(options.outputPath);
|
|
100
|
+
await mkdir(dirname(absoluteOutputPath), { recursive: true });
|
|
101
|
+
await writeFile(absoluteOutputPath, output, "utf8");
|
|
102
|
+
process.stdout.write(`Report written to ${absoluteOutputPath}\n`);
|
|
103
|
+
}
|
|
104
|
+
if (report.summary.critical > 0 || report.summary.high > 0) {
|
|
105
|
+
process.exitCode = 2;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
main().catch((error) => {
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
111
|
+
process.exitCode = 1;
|
|
112
|
+
});
|
|
113
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { RESERVED_ENV_KEYS } from "./environment.js";
|
|
4
|
+
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
5
|
+
const DEFAULT_MAX_TOOLS = 100;
|
|
6
|
+
const MAX_TIMEOUT_MS = 60_000;
|
|
7
|
+
const MAX_TOOLS = 1_000;
|
|
8
|
+
const ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
9
|
+
function assertRecord(value, label) {
|
|
10
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
11
|
+
throw new Error(`${label} must be a JSON object.`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function readPositiveInteger(value, fallback, maximum, label) {
|
|
15
|
+
if (value === undefined) {
|
|
16
|
+
return fallback;
|
|
17
|
+
}
|
|
18
|
+
if (!Number.isInteger(value) || value <= 0 || value > maximum) {
|
|
19
|
+
throw new Error(`${label} must be an integer between 1 and ${maximum}.`);
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function readTargetEnvironment(value) {
|
|
24
|
+
assertRecord(value, "target.env");
|
|
25
|
+
const environment = {};
|
|
26
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
27
|
+
if (!ENV_KEY_PATTERN.test(key)) {
|
|
28
|
+
throw new Error(`target.env['${key}'] is not a valid environment variable name.`);
|
|
29
|
+
}
|
|
30
|
+
if (RESERVED_ENV_KEYS.has(key.toUpperCase())) {
|
|
31
|
+
throw new Error(`target.env['${key}'] is reserved by the sanitized environment and cannot be overridden.`);
|
|
32
|
+
}
|
|
33
|
+
if (typeof entry !== "string") {
|
|
34
|
+
throw new Error(`target.env['${key}'] must be a string.`);
|
|
35
|
+
}
|
|
36
|
+
environment[key] = entry;
|
|
37
|
+
}
|
|
38
|
+
return environment;
|
|
39
|
+
}
|
|
40
|
+
export async function loadConfig(configPath) {
|
|
41
|
+
const absoluteConfigPath = resolve(configPath);
|
|
42
|
+
const raw = await readFile(absoluteConfigPath, "utf8");
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = JSON.parse(raw);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
49
|
+
throw new Error(`Config is not valid JSON: ${message}`, { cause: error });
|
|
50
|
+
}
|
|
51
|
+
assertRecord(parsed, "Config");
|
|
52
|
+
assertRecord(parsed.target, "target");
|
|
53
|
+
const { command, args = [], cwd = ".", env = {}, url, transport = "http" } = parsed.target;
|
|
54
|
+
if (url !== undefined && typeof url !== "string") {
|
|
55
|
+
throw new Error("target.url must be a string if provided.");
|
|
56
|
+
}
|
|
57
|
+
if (transport !== "http" && transport !== "sse") {
|
|
58
|
+
throw new Error('target.transport must be "http" or "sse".');
|
|
59
|
+
}
|
|
60
|
+
if (url === undefined) {
|
|
61
|
+
if (typeof command !== "string" || command.trim() === "") {
|
|
62
|
+
throw new Error("target.command must be a non-empty string when url is not provided.");
|
|
63
|
+
}
|
|
64
|
+
if (!Array.isArray(args) || args.some((arg) => typeof arg !== "string")) {
|
|
65
|
+
throw new Error("target.args must be an array of strings.");
|
|
66
|
+
}
|
|
67
|
+
if (typeof cwd !== "string" || cwd.trim() === "") {
|
|
68
|
+
throw new Error("target.cwd must be a non-empty string.");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const policyValue = parsed.policy ?? {};
|
|
72
|
+
assertRecord(policyValue, "policy");
|
|
73
|
+
return {
|
|
74
|
+
target: {
|
|
75
|
+
...(url !== undefined
|
|
76
|
+
? { url: url, transport: transport }
|
|
77
|
+
: {
|
|
78
|
+
command: command,
|
|
79
|
+
args: [...args],
|
|
80
|
+
cwd: resolve(dirname(absoluteConfigPath), cwd),
|
|
81
|
+
}),
|
|
82
|
+
env: readTargetEnvironment(env),
|
|
83
|
+
},
|
|
84
|
+
policy: {
|
|
85
|
+
timeoutMs: readPositiveInteger(policyValue.timeoutMs, DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS, "policy.timeoutMs"),
|
|
86
|
+
maxTools: readPositiveInteger(policyValue.maxTools, DEFAULT_MAX_TOOLS, MAX_TOOLS, "policy.maxTools"),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const RESERVED_ENV_KEYS: ReadonlySet<string>;
|
|
2
|
+
export declare function createSanitizedEnvironment(source?: NodeJS.ProcessEnv): Record<string, string>;
|
|
3
|
+
/** Strong, low-false-positive test used to RAISE a credentials-in-URL finding. */
|
|
4
|
+
export declare function isCredentialQueryKey(key: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Remove credentials from a URL before it enters a report: strip any userinfo
|
|
7
|
+
* (`user:pass@`) and redact the value of sensitive query AND fragment parameters
|
|
8
|
+
* (OAuth implicit-flow tokens live in the fragment). Falls back to a coarse regex
|
|
9
|
+
* when the string is not a parseable URL.
|
|
10
|
+
*/
|
|
11
|
+
export declare function redactUrl(rawUrl: string): string;
|
|
12
|
+
export declare function redactArguments(args: string[]): string[];
|