@elliotllliu/agentshield 0.1.0 → 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/README.md CHANGED
@@ -7,7 +7,7 @@ Catch data exfiltration, backdoors, privilege escalation, and supply chain vulne
7
7
  ## Quick Start
8
8
 
9
9
  ```bash
10
- npx agentshield scan ./my-skill/
10
+ npx @elliotllliu/agentshield scan ./my-skill/
11
11
  ```
12
12
 
13
13
  ## What It Detects
@@ -29,6 +29,8 @@ npx agentshield scan ./my-skill/
29
29
  | `sensitive-read` | 🟡 Warning | Accesses `~/.ssh/id_rsa`, `~/.aws/credentials`, etc. |
30
30
  | `excessive-perms` | 🟡 Warning | Too many or dangerous permissions in SKILL.md |
31
31
  | `phone-home` | 🟡 Warning | Periodic timers + HTTP requests (beacon/heartbeat pattern) |
32
+ | `mcp-manifest` | 🟡 Warning | MCP server: wildcard perms, undeclared capabilities, suspicious tool descriptions |
33
+ | `mcp-manifest` | 🟡 Warning | MCP server tool/resource declarations vs actual code behavior |
32
34
 
33
35
  ## Example Output
34
36
 
@@ -63,18 +65,97 @@ agentshield scan ./skill/ --json
63
65
  # Fail CI if score is below threshold
64
66
  agentshield scan ./skill/ --fail-under 70
65
67
 
68
+ # Disable specific rules
69
+ agentshield scan ./skill/ --disable supply-chain,phone-home
70
+
71
+ # Only run specific rules
72
+ agentshield scan ./skill/ --enable backdoor,data-exfil
73
+
66
74
  # Shorthand (directory as first arg)
67
75
  agentshield ./skill/
76
+
77
+ # Generate config files
78
+ agentshield init
79
+
80
+ # Watch mode (re-scan on changes)
81
+ agentshield watch ./skill/
82
+
83
+ # Compare two versions
84
+ agentshield compare ./skill-v1/ ./skill-v2/
85
+ ```
86
+
87
+ ## Configuration
88
+
89
+ Create `.agentshield.yml` in your project (or run `agentshield init`):
90
+
91
+ ```yaml
92
+ rules:
93
+ disable:
94
+ - supply-chain # skip npm audit
95
+ - phone-home # allow periodic HTTP
96
+
97
+ severity:
98
+ sensitive-read: info # downgrade to info
99
+
100
+ failUnder: 70 # CI threshold
101
+
102
+ ignore:
103
+ - "tests/**"
104
+ - "*.test.ts"
105
+ ```
106
+
107
+ ### `.agentshieldignore`
108
+
109
+ Exclude files from scanning (same syntax as `.gitignore`):
110
+
111
+ ```
112
+ node_modules/
113
+ dist/
114
+ *.test.ts
115
+ __tests__/
68
116
  ```
69
117
 
70
118
  ## CI Integration
71
119
 
120
+ ### GitHub Action (recommended)
121
+
122
+ ```yaml
123
+ # .github/workflows/security.yml
124
+ name: Security Scan
125
+ on: [push, pull_request]
126
+ jobs:
127
+ scan:
128
+ runs-on: ubuntu-latest
129
+ steps:
130
+ - uses: actions/checkout@v4
131
+ - uses: elliotllliu/agentshield@main
132
+ with:
133
+ path: './skills/'
134
+ fail-under: '70'
135
+ ```
136
+
137
+ ### npx one-liner
138
+
72
139
  ```yaml
73
- # GitHub Actions
74
140
  - name: Security scan
75
- run: npx agentshield scan ./skills/ --fail-under 70
141
+ run: npx -y @elliotllliu/agentshield scan ./skills/ --fail-under 70
76
142
  ```
77
143
 
144
+ ### Action Inputs
145
+
146
+ | Input | Default | Description |
147
+ |-------|---------|-------------|
148
+ | `path` | `.` | Directory to scan |
149
+ | `fail-under` | *(none)* | Fail if score is below threshold (0-100) |
150
+ | `format` | `terminal` | Output format: `terminal` or `json` |
151
+
152
+ ### Action Outputs
153
+
154
+ | Output | Description |
155
+ |--------|-------------|
156
+ | `score` | Security score (0-100) |
157
+ | `findings` | Number of findings |
158
+
78
159
  ## Scoring
79
160
 
80
161
  Starts at 100, deducts per finding:
@@ -0,0 +1,66 @@
1
+ # 🛡️ AgentShield
2
+
3
+ AI Agent 技能/插件安全扫描器
4
+
5
+ 在安装第三方 AI 技能之前,扫描数据窃取、后门、权限越界和供应链漏洞。
6
+
7
+ ## 快速开始
8
+
9
+ ```bash
10
+ npx @elliotllliu/agentshield scan ./my-skill/
11
+ ```
12
+
13
+ ## 检测能力
14
+
15
+ | 规则 | 级别 | 说明 |
16
+ |------|------|------|
17
+ | `data-exfil` | 🔴 严重 | 读取敏感文件 + 发送 HTTP 请求(数据外泄) |
18
+ | `backdoor` | 🔴 严重 | `eval()`、`exec()`、动态代码执行 |
19
+ | `reverse-shell` | 🔴 严重 | Socket 外连 + Shell 管道(反弹 Shell) |
20
+ | `crypto-mining` | 🔴 严重 | 挖矿池连接、已知挖矿程序 |
21
+ | `credential-hardcode` | 🔴 严重 | 硬编码 AWS Key、GitHub PAT、Stripe Key |
22
+ | `env-leak` | 🔴 严重 | 环境变量读取 + HTTP 外发 |
23
+ | `obfuscation` | 🔴 严重 | base64+eval 混淆、十六进制编码 |
24
+ | `typosquatting` | 🔴 严重 | npm 包名拼写仿冒(如 `1odash`) |
25
+ | `hidden-files` | 🔴 严重 | `.env` 文件包含明文密钥 |
26
+ | `network-ssrf` | 🟡 警告 | 用户可控 URL、SSRF、AWS 元数据端点 |
27
+ | `privilege` | 🟡 警告 | SKILL.md 声明权限 vs 代码实际行为不匹配 |
28
+ | `supply-chain` | 🟡 警告 | npm 依赖已知 CVE 漏洞 |
29
+ | `sensitive-read` | 🟡 警告 | 读取 SSH 密钥、AWS 凭证等 |
30
+ | `excessive-perms` | 🟡 警告 | 权限声明过多或过于危险 |
31
+ | `phone-home` | 🟡 警告 | 定时器 + HTTP 请求(心跳/信标模式) |
32
+
33
+ ## 使用方法
34
+
35
+ ```bash
36
+ # 扫描目录
37
+ npx @elliotllliu/agentshield scan ./path/to/skill/
38
+
39
+ # JSON 输出(适用于 CI/CD)
40
+ npx @elliotllliu/agentshield scan ./skill/ --json
41
+
42
+ # CI 门禁:分数低于阈值则失败
43
+ npx @elliotllliu/agentshield scan ./skill/ --fail-under 70
44
+ ```
45
+
46
+ ## GitHub Actions 集成
47
+
48
+ ```yaml
49
+ - uses: elliotllliu/agentshield@main
50
+ with:
51
+ path: './skills/'
52
+ fail-under: '70'
53
+ ```
54
+
55
+ ## 安全评分
56
+
57
+ | 分数 | 风险等级 |
58
+ |------|----------|
59
+ | 90-100 | 低风险 ✅ |
60
+ | 70-89 | 中等风险 🟡 |
61
+ | 40-69 | 高风险 🟠 |
62
+ | 0-39 | 严重风险 🔴 |
63
+
64
+ ## 许可证
65
+
66
+ MIT
package/dist/cli.js CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
- import { resolve } from "path";
4
- import { existsSync, statSync } from "fs";
3
+ import { resolve, join } from "path";
4
+ import { existsSync, statSync, writeFileSync, watch as fsWatch, mkdirSync } from "fs";
5
5
  import { scan } from "./scanner/index.js";
6
6
  import { printReport } from "./reporter/terminal.js";
7
7
  import { printJsonReport } from "./reporter/json.js";
8
+ import { generateBadgeSvg, generateBadgeMarkdown } from "./reporter/badge.js";
9
+ import { DEFAULT_CONFIG, DEFAULT_IGNORE } from "./config.js";
8
10
  const program = new Command();
9
11
  program
10
12
  .name("agentshield")
@@ -16,27 +18,195 @@ program
16
18
  .argument("<directory>", "Target directory to scan")
17
19
  .option("--json", "Output results as JSON")
18
20
  .option("--fail-under <score>", "Exit with code 1 if score is below threshold", parseInt)
21
+ .option("--disable <rules>", "Comma-separated rules to disable")
22
+ .option("--enable <rules>", "Comma-separated rules to enable (only these)")
19
23
  .action((directory, options) => {
20
24
  const target = resolve(directory);
21
25
  if (!existsSync(target) || !statSync(target).isDirectory()) {
22
26
  console.error(`Error: "${directory}" is not a valid directory`);
23
27
  process.exit(1);
24
28
  }
25
- const result = scan(target);
29
+ const configOverride = {};
30
+ if (options.disable || options.enable) {
31
+ configOverride.rules = {};
32
+ if (options.disable) {
33
+ configOverride.rules.disable = options.disable.split(",").map((s) => s.trim());
34
+ }
35
+ if (options.enable) {
36
+ configOverride.rules.enable = options.enable.split(",").map((s) => s.trim());
37
+ }
38
+ }
39
+ const result = scan(target, configOverride);
26
40
  if (options.json) {
27
41
  printJsonReport(result);
28
42
  }
29
43
  else {
30
44
  printReport(result);
31
45
  }
46
+ const threshold = options.failUnder ?? result.score;
32
47
  if (options.failUnder !== undefined && result.score < options.failUnder) {
33
48
  process.exit(1);
34
49
  }
35
50
  });
51
+ program
52
+ .command("init")
53
+ .description("Generate .agentshield.yml and .agentshieldignore config files")
54
+ .argument("[directory]", "Target directory", ".")
55
+ .action((directory) => {
56
+ const target = resolve(directory);
57
+ if (!existsSync(target)) {
58
+ mkdirSync(target, { recursive: true });
59
+ }
60
+ const configPath = join(target, ".agentshield.yml");
61
+ const ignorePath = join(target, ".agentshieldignore");
62
+ if (existsSync(configPath)) {
63
+ console.log(`⚠️ ${configPath} already exists, skipping`);
64
+ }
65
+ else {
66
+ writeFileSync(configPath, DEFAULT_CONFIG);
67
+ console.log(`✅ Created ${configPath}`);
68
+ }
69
+ if (existsSync(ignorePath)) {
70
+ console.log(`⚠️ ${ignorePath} already exists, skipping`);
71
+ }
72
+ else {
73
+ writeFileSync(ignorePath, DEFAULT_IGNORE);
74
+ console.log(`✅ Created ${ignorePath}`);
75
+ }
76
+ });
77
+ program
78
+ .command("watch")
79
+ .description("Watch a directory and re-scan on file changes")
80
+ .argument("<directory>", "Target directory to watch")
81
+ .option("--json", "Output results as JSON")
82
+ .action((directory, options) => {
83
+ const target = resolve(directory);
84
+ if (!existsSync(target) || !statSync(target).isDirectory()) {
85
+ console.error(`Error: "${directory}" is not a valid directory`);
86
+ process.exit(1);
87
+ }
88
+ console.log(`👀 Watching ${target} for changes... (Ctrl+C to stop)\n`);
89
+ const runScan = () => {
90
+ console.clear();
91
+ console.log(`👀 Watching ${target} — last scan: ${new Date().toLocaleTimeString()}\n`);
92
+ const result = scan(target);
93
+ if (options.json) {
94
+ printJsonReport(result);
95
+ }
96
+ else {
97
+ printReport(result);
98
+ }
99
+ };
100
+ // Initial scan
101
+ runScan();
102
+ // Watch for changes
103
+ try {
104
+ const watcher = fsWatch(target, { recursive: true }, () => {
105
+ runScan();
106
+ });
107
+ process.on("SIGINT", () => {
108
+ watcher.close();
109
+ process.exit(0);
110
+ });
111
+ }
112
+ catch {
113
+ console.error("⚠️ fs.watch recursive not supported on this platform. Use: nodemon --exec 'agentshield scan .'");
114
+ }
115
+ });
116
+ program
117
+ .command("compare")
118
+ .description("Compare security scores between two directories or git refs")
119
+ .argument("<dirA>", "First directory")
120
+ .argument("<dirB>", "Second directory")
121
+ .option("--json", "Output as JSON")
122
+ .action((dirA, dirB, options) => {
123
+ const targetA = resolve(dirA);
124
+ const targetB = resolve(dirB);
125
+ for (const [label, dir] of [["A", targetA], ["B", targetB]]) {
126
+ if (!existsSync(dir) || !statSync(dir).isDirectory()) {
127
+ console.error(`Error: directory ${label} "${dir}" is not valid`);
128
+ process.exit(1);
129
+ }
130
+ }
131
+ const resultA = scan(targetA);
132
+ const resultB = scan(targetB);
133
+ if (options.json) {
134
+ console.log(JSON.stringify({
135
+ before: { target: resultA.target, score: resultA.score, findings: resultA.findings.length },
136
+ after: { target: resultB.target, score: resultB.score, findings: resultB.findings.length },
137
+ delta: resultB.score - resultA.score,
138
+ }, null, 2));
139
+ return;
140
+ }
141
+ console.log("\n🔄 AgentShield Comparison\n");
142
+ console.log(` A: ${dirA} — Score: ${resultA.score}/100 (${resultA.findings.length} findings)`);
143
+ console.log(` B: ${dirB} — Score: ${resultB.score}/100 (${resultB.findings.length} findings)`);
144
+ console.log();
145
+ const delta = resultB.score - resultA.score;
146
+ if (delta > 0) {
147
+ console.log(` ✅ Improved by ${delta} points`);
148
+ }
149
+ else if (delta < 0) {
150
+ console.log(` 🔴 Degraded by ${Math.abs(delta)} points`);
151
+ }
152
+ else {
153
+ console.log(` ➡️ No change`);
154
+ }
155
+ // Show new findings in B that aren't in A
156
+ const aKeys = new Set(resultA.findings.map((f) => `${f.rule}:${f.file}:${f.line}`));
157
+ const newFindings = resultB.findings.filter((f) => !aKeys.has(`${f.rule}:${f.file}:${f.line}`));
158
+ const fixedFindings = resultA.findings.filter((f) => {
159
+ const bKeys = new Set(resultB.findings.map((bf) => `${bf.rule}:${bf.file}:${bf.line}`));
160
+ return !bKeys.has(`${f.rule}:${f.file}:${f.line}`);
161
+ });
162
+ if (newFindings.length > 0) {
163
+ console.log(`\n 🆕 New findings (${newFindings.length}):`);
164
+ for (const f of newFindings.slice(0, 10)) {
165
+ console.log(` ${f.file}${f.line ? `:${f.line}` : ""} — [${f.rule}] ${f.message}`);
166
+ }
167
+ }
168
+ if (fixedFindings.length > 0) {
169
+ console.log(`\n ✅ Fixed (${fixedFindings.length}):`);
170
+ for (const f of fixedFindings.slice(0, 10)) {
171
+ console.log(` ${f.file}${f.line ? `:${f.line}` : ""} — [${f.rule}] ${f.message}`);
172
+ }
173
+ }
174
+ console.log();
175
+ });
176
+ program
177
+ .command("badge")
178
+ .description("Generate a security badge for your project")
179
+ .argument("<directory>", "Target directory to scan")
180
+ .option("--svg", "Output raw SVG")
181
+ .option("--markdown", "Output markdown badge (default)")
182
+ .option("-o, --output <file>", "Save SVG to file")
183
+ .action((directory, options) => {
184
+ const target = resolve(directory);
185
+ if (!existsSync(target) || !statSync(target).isDirectory()) {
186
+ console.error(`Error: "${directory}" is not a valid directory`);
187
+ process.exit(1);
188
+ }
189
+ const result = scan(target);
190
+ if (options.svg || options.output) {
191
+ const svg = generateBadgeSvg(result);
192
+ if (options.output) {
193
+ writeFileSync(resolve(options.output), svg);
194
+ console.log(`✅ Badge saved to ${options.output}`);
195
+ }
196
+ else {
197
+ console.log(svg);
198
+ }
199
+ }
200
+ else {
201
+ // Default: markdown
202
+ const md = generateBadgeMarkdown(result.score);
203
+ console.log(md);
204
+ console.log(`\nPaste this in your README.md to show the badge.`);
205
+ }
206
+ });
36
207
  // Default: if first arg looks like a directory, treat as scan
37
208
  const args = process.argv.slice(2);
38
- if (args.length > 0 && !args[0].startsWith("-") && args[0] !== "scan" && args[0] !== "help") {
39
- // Rewrite: `agentshield ./dir` → `agentshield scan ./dir`
209
+ if (args.length > 0 && !args[0].startsWith("-") && !["scan", "init", "watch", "compare", "badge", "help"].includes(args[0])) {
40
210
  process.argv.splice(2, 0, "scan");
41
211
  }
42
212
  program.parse();
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CAAC,gEAAgE,CAAC;KAC7E,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,mDAAmD,CAAC;KAChE,QAAQ,CAAC,aAAa,EAAE,0BAA0B,CAAC;KACnD,MAAM,CAAC,QAAQ,EAAE,wBAAwB,CAAC;KAC1C,MAAM,CAAC,sBAAsB,EAAE,8CAA8C,EAAE,QAAQ,CAAC;KACxF,MAAM,CAAC,CAAC,SAAiB,EAAE,OAA+C,EAAE,EAAE;IAC7E,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAElC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QAC3D,OAAO,CAAC,KAAK,CAAC,WAAW,SAAS,4BAA4B,CAAC,CAAC;QAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAE5B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,eAAe,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,WAAW,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;IAED,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,8DAA8D;AAC9D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;IAC7F,0DAA0D;IAC1D,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /** AgentShield configuration */
2
+ export interface ScanConfig {
3
+ /** Rules to enable (default: all) */
4
+ rules?: {
5
+ enable?: string[];
6
+ disable?: string[];
7
+ };
8
+ /** Severity overrides: rule-id → severity */
9
+ severity?: Record<string, "critical" | "warning" | "info">;
10
+ /** Score threshold for CI (same as --fail-under) */
11
+ failUnder?: number;
12
+ /** Glob patterns to ignore */
13
+ ignore?: string[];
14
+ }
15
+ /** Load config from target directory or parents */
16
+ export declare function loadConfig(dir: string): ScanConfig;
17
+ /** Load .agentshieldignore patterns */
18
+ export declare function loadIgnorePatterns(dir: string): string[];
19
+ /** Check if a file path matches any ignore pattern */
20
+ export declare function isIgnored(filePath: string, patterns: string[]): boolean;
21
+ /** Default config content for `agentshield init` */
22
+ export declare const DEFAULT_CONFIG = "# AgentShield Configuration\n# https://github.com/elliotllliu/agentshield\n\nrules:\n # disable:\n # - supply-chain # skip npm audit\n # - phone-home # allow periodic HTTP\n\n# severity:\n# sensitive-read: info # downgrade to info\n\n# failUnder: 70 # CI threshold\n\n# ignore:\n# - \"tests/**\"\n# - \"*.test.ts\"\n";
23
+ /** Default ignore content */
24
+ export declare const DEFAULT_IGNORE = "# AgentShield Ignore\n# Patterns here will be excluded from scanning\n\nnode_modules/\ndist/\nbuild/\n.git/\n*.test.ts\n*.test.js\n*.spec.ts\n*.spec.js\n__tests__/\ncoverage/\n";
package/dist/config.js ADDED
@@ -0,0 +1,91 @@
1
+ import { readFileSync, existsSync } from "fs";
2
+ import { join } from "path";
3
+ import { parse as parseYaml } from "./yaml-simple.js";
4
+ const CONFIG_NAMES = [".agentshield.yml", ".agentshield.yaml", "agentshield.config.yml"];
5
+ /** Load config from target directory or parents */
6
+ export function loadConfig(dir) {
7
+ for (const name of CONFIG_NAMES) {
8
+ const configPath = join(dir, name);
9
+ if (existsSync(configPath)) {
10
+ try {
11
+ const content = readFileSync(configPath, "utf-8");
12
+ return parseYaml(content);
13
+ }
14
+ catch {
15
+ // invalid config, use defaults
16
+ }
17
+ }
18
+ }
19
+ return {};
20
+ }
21
+ /** Load .agentshieldignore patterns */
22
+ export function loadIgnorePatterns(dir) {
23
+ const ignorePath = join(dir, ".agentshieldignore");
24
+ if (!existsSync(ignorePath))
25
+ return [];
26
+ try {
27
+ return readFileSync(ignorePath, "utf-8")
28
+ .split("\n")
29
+ .map((line) => line.trim())
30
+ .filter((line) => line && !line.startsWith("#"));
31
+ }
32
+ catch {
33
+ return [];
34
+ }
35
+ }
36
+ /** Check if a file path matches any ignore pattern */
37
+ export function isIgnored(filePath, patterns) {
38
+ for (const pattern of patterns) {
39
+ // Simple glob matching: support * and **
40
+ if (pattern.endsWith("/")) {
41
+ // Directory pattern
42
+ if (filePath.startsWith(pattern) || filePath.includes("/" + pattern))
43
+ return true;
44
+ }
45
+ else if (pattern.includes("*")) {
46
+ const regex = new RegExp("^" + pattern.replace(/\./g, "\\.").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*") + "$");
47
+ if (regex.test(filePath))
48
+ return true;
49
+ }
50
+ else {
51
+ // Exact match or suffix match
52
+ if (filePath === pattern || filePath.endsWith("/" + pattern) || filePath.endsWith(pattern))
53
+ return true;
54
+ }
55
+ }
56
+ return false;
57
+ }
58
+ /** Default config content for `agentshield init` */
59
+ export const DEFAULT_CONFIG = `# AgentShield Configuration
60
+ # https://github.com/elliotllliu/agentshield
61
+
62
+ rules:
63
+ # disable:
64
+ # - supply-chain # skip npm audit
65
+ # - phone-home # allow periodic HTTP
66
+
67
+ # severity:
68
+ # sensitive-read: info # downgrade to info
69
+
70
+ # failUnder: 70 # CI threshold
71
+
72
+ # ignore:
73
+ # - "tests/**"
74
+ # - "*.test.ts"
75
+ `;
76
+ /** Default ignore content */
77
+ export const DEFAULT_IGNORE = `# AgentShield Ignore
78
+ # Patterns here will be excluded from scanning
79
+
80
+ node_modules/
81
+ dist/
82
+ build/
83
+ .git/
84
+ *.test.ts
85
+ *.test.js
86
+ *.spec.ts
87
+ *.spec.js
88
+ __tests__/
89
+ coverage/
90
+ `;
91
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAiBtD,MAAM,YAAY,GAAG,CAAC,kBAAkB,EAAE,mBAAmB,EAAE,wBAAwB,CAAC,CAAC;AAEzF,mDAAmD;AACnD,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;gBAClD,OAAO,SAAS,CAAC,OAAO,CAAe,CAAC;YAC1C,CAAC;YAAC,MAAM,CAAC;gBACP,+BAA+B;YACjC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;IACnD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC;aACrC,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;aAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,QAAkB;IAC5D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,yCAAyC;QACzC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,oBAAoB;YACpB,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,GAAG,OAAO,CAAC;gBAAE,OAAO,IAAI,CAAC;QACpF,CAAC;aAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,IAAI,MAAM,CACtB,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,GAAG,CACzF,CAAC;YACF,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,OAAO,IAAI,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,8BAA8B;YAC9B,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,GAAG,OAAO,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC1G,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;CAgB7B,CAAC;AAEF,6BAA6B;AAC7B,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;CAa7B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { ScanResult } from "../types.js";
2
+ /**
3
+ * Generate a shields.io-style SVG badge for the security score.
4
+ */
5
+ export declare function generateBadgeSvg(result: ScanResult): string;
6
+ /** Generate a markdown badge string */
7
+ export declare function generateBadgeMarkdown(score: number, repoUrl?: string): string;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Generate a shields.io-style SVG badge for the security score.
3
+ */
4
+ export function generateBadgeSvg(result) {
5
+ const score = result.score;
6
+ const { color, label } = getBadgeStyle(score);
7
+ const scoreText = `${score}/100`;
8
+ // Shield dimensions
9
+ const labelWidth = 90;
10
+ const valueWidth = 60;
11
+ const totalWidth = labelWidth + valueWidth;
12
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${totalWidth}" height="20" role="img" aria-label="AgentShield: ${scoreText}">
13
+ <title>AgentShield: ${scoreText} (${label})</title>
14
+ <linearGradient id="s" x2="0" y2="100%">
15
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
16
+ <stop offset="1" stop-opacity=".1"/>
17
+ </linearGradient>
18
+ <clipPath id="r">
19
+ <rect width="${totalWidth}" height="20" rx="3" fill="#fff"/>
20
+ </clipPath>
21
+ <g clip-path="url(#r)">
22
+ <rect width="${labelWidth}" height="20" fill="#555"/>
23
+ <rect x="${labelWidth}" width="${valueWidth}" height="20" fill="${color}"/>
24
+ <rect width="${totalWidth}" height="20" fill="url(#s)"/>
25
+ </g>
26
+ <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110">
27
+ <text aria-hidden="true" x="${labelWidth * 5}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)">${"🛡️ AgentShield"}</text>
28
+ <text x="${labelWidth * 5}" y="140" transform="scale(.1)">${"🛡️ AgentShield"}</text>
29
+ <text aria-hidden="true" x="${(labelWidth + valueWidth / 2) * 10}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)">${scoreText}</text>
30
+ <text x="${(labelWidth + valueWidth / 2) * 10}" y="140" transform="scale(.1)">${scoreText}</text>
31
+ </g>
32
+ </svg>`;
33
+ }
34
+ /** Generate a markdown badge string */
35
+ export function generateBadgeMarkdown(score, repoUrl) {
36
+ const { color, label } = getBadgeStyle(score);
37
+ const badgeUrl = `https://img.shields.io/badge/AgentShield-${score}%2F100-${color.replace("#", "")}?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0xMiAxTDMgNXY2YzAgNS41NSAzLjg0IDEwLjc0IDkgMTIgNS4xNi0xLjI2IDktNi40NSA5LTEyVjVsLTktNHoiLz48L3N2Zz4=`;
38
+ const link = repoUrl || "https://github.com/elliotllliu/agentshield";
39
+ return `[![AgentShield ${score}/100](${badgeUrl})](${link})`;
40
+ }
41
+ function getBadgeStyle(score) {
42
+ if (score >= 90)
43
+ return { color: "#4c1", label: "Low Risk" };
44
+ if (score >= 70)
45
+ return { color: "#dfb317", label: "Moderate Risk" };
46
+ if (score >= 40)
47
+ return { color: "#fe7d37", label: "High Risk" };
48
+ return { color: "#e05d44", label: "Critical Risk" };
49
+ }
50
+ //# sourceMappingURL=badge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badge.js","sourceRoot":"","sources":["../../src/reporter/badge.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAkB;IACjD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC3B,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,GAAG,KAAK,MAAM,CAAC;IAEjC,oBAAoB;IACpB,MAAM,UAAU,GAAG,EAAE,CAAC;IACtB,MAAM,UAAU,GAAG,EAAE,CAAC;IACtB,MAAM,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;IAE3C,OAAO,kDAAkD,UAAU,qDAAqD,SAAS;wBAC3G,SAAS,KAAK,KAAK;;;;;;mBAMxB,UAAU;;;mBAGV,UAAU;eACd,UAAU,YAAY,UAAU,uBAAuB,KAAK;mBACxD,UAAU;;;kCAGK,UAAU,GAAG,CAAC,oEAAoE,iBAAiB;eACtH,UAAU,GAAG,CAAC,mCAAmC,iBAAiB;kCAC/C,CAAC,UAAU,GAAG,UAAU,GAAG,CAAC,CAAC,GAAG,EAAE,oEAAoE,SAAS;eAClI,CAAC,UAAU,GAAG,UAAU,GAAG,CAAC,CAAC,GAAG,EAAE,mCAAmC,SAAS;;OAEtF,CAAC;AACR,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,qBAAqB,CAAC,KAAa,EAAE,OAAgB;IACnE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,4CAA4C,KAAK,UAAU,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,oPAAoP,CAAC;IACvV,MAAM,IAAI,GAAG,OAAO,IAAI,4CAA4C,CAAC;IACrE,OAAO,kBAAkB,KAAK,SAAS,QAAQ,MAAM,IAAI,GAAG,CAAC;AAC/D,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAClC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAC7D,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;IACrE,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IACjE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;AACtD,CAAC"}
@@ -13,6 +13,7 @@ import { excessivePermsRule } from "./excessive-perms.js";
13
13
  import { phoneHomeRule } from "./phone-home.js";
14
14
  import { credentialHardcodeRule } from "./credential-hardcode.js";
15
15
  import { networkSsrfRule } from "./network-ssrf.js";
16
+ import { mcpManifestRule } from "./mcp-manifest.js";
16
17
  /** All registered rules */
17
18
  export const rules = [
18
19
  // Original 5
@@ -32,6 +33,7 @@ export const rules = [
32
33
  phoneHomeRule,
33
34
  credentialHardcodeRule,
34
35
  networkSsrfRule,
36
+ mcpManifestRule,
35
37
  ];
36
38
  /** Get a rule by ID */
37
39
  export function getRule(id) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,2BAA2B;AAC3B,MAAM,CAAC,MAAM,KAAK,GAAW;IAC3B,aAAa;IACb,aAAa;IACb,YAAY;IACZ,aAAa;IACb,eAAe;IACf,iBAAiB;IACjB,SAAS;IACT,eAAe;IACf,WAAW;IACX,gBAAgB;IAChB,gBAAgB;IAChB,iBAAiB;IACjB,eAAe;IACf,kBAAkB;IAClB,aAAa;IACb,sBAAsB;IACtB,eAAe;CAChB,CAAC;AAEF,uBAAuB;AACvB,MAAM,UAAU,OAAO,CAAC,EAAU;IAChC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AACxC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,2BAA2B;AAC3B,MAAM,CAAC,MAAM,KAAK,GAAW;IAC3B,aAAa;IACb,aAAa;IACb,YAAY;IACZ,aAAa;IACb,eAAe;IACf,iBAAiB;IACjB,SAAS;IACT,eAAe;IACf,WAAW;IACX,gBAAgB;IAChB,gBAAgB;IAChB,iBAAiB;IACjB,eAAe;IACf,kBAAkB;IAClB,aAAa;IACb,sBAAsB;IACtB,eAAe;IACf,eAAe;CAChB,CAAC;AAEF,uBAAuB;AACvB,MAAM,UAAU,OAAO,CAAC,EAAU;IAChC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "../types.js";
2
+ export declare const mcpManifestRule: Rule;
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Rule: mcp-manifest
3
+ * Validates MCP (Model Context Protocol) server configurations.
4
+ *
5
+ * Checks:
6
+ * 1. Declared tools/resources vs actual code behavior
7
+ * 2. Overly broad tool descriptions that could mislead agents
8
+ * 3. Undeclared file system / network / exec capabilities
9
+ * 4. Suspicious tool names or descriptions
10
+ */
11
+ // Patterns indicating MCP server tool registration
12
+ const TOOL_REGISTER_RE = /\.tool\s*\(|addTool\s*\(|registerTool\s*\(|server\.setRequestHandler.*ListTools|tools:\s*\[/;
13
+ // Patterns for MCP resource registration
14
+ const RESOURCE_REGISTER_RE = /\.resource\s*\(|addResource\s*\(|registerResource\s*\(|server\.setRequestHandler.*ListResources|resources:\s*\[/;
15
+ // Dangerous patterns in tool implementations
16
+ const DANGEROUS_TOOL_PATTERNS = [
17
+ { pattern: /child_process|execSync|exec\(|spawn\(/, desc: "Tool executes shell commands", severity: "critical" },
18
+ { pattern: /fs\.unlink|fs\.rmdir|fs\.rm\b|rimraf/, desc: "Tool deletes files", severity: "warning" },
19
+ { pattern: /fs\.writeFile|fs\.appendFile|fs\.createWriteStream/, desc: "Tool writes to file system", severity: "warning" },
20
+ { pattern: /fetch\s*\(|axios|http\.request|https\.request/, desc: "Tool makes outbound HTTP requests", severity: "warning" },
21
+ { pattern: /eval\s*\(|new\s+Function\s*\(/, desc: "Tool uses dynamic code execution", severity: "critical" },
22
+ { pattern: /\.ssh|\.aws|\.env\b|credentials|secret/i, desc: "Tool accesses sensitive paths/credentials", severity: "critical" },
23
+ ];
24
+ // Suspicious tool name/description patterns
25
+ const SUSPICIOUS_TOOL_DESC = [
26
+ { pattern: /run.*any.*command|execute.*arbitrary|shell.*access/i, desc: "Tool claims unrestricted command execution" },
27
+ { pattern: /access.*all.*files|read.*entire.*filesystem/i, desc: "Tool claims full filesystem access" },
28
+ { pattern: /send.*data.*to|upload.*to|transmit.*to/i, desc: "Tool description mentions data transmission" },
29
+ { pattern: /modify.*system|change.*config/i, desc: "Tool claims system modification capability" },
30
+ ];
31
+ export const mcpManifestRule = {
32
+ id: "mcp-manifest",
33
+ name: "MCP Server Validation",
34
+ description: "Validates MCP server tool/resource declarations against actual code behavior",
35
+ run(files) {
36
+ const findings = [];
37
+ // Detect if this is an MCP server project
38
+ const isMcpServer = detectMcpServer(files);
39
+ if (!isMcpServer)
40
+ return findings;
41
+ // Check for MCP manifest/config files
42
+ checkMcpConfig(files, findings);
43
+ // Analyze tool implementations for dangerous patterns
44
+ checkToolImplementations(files, findings);
45
+ // Check tool descriptions for suspicious claims
46
+ checkToolDescriptions(files, findings);
47
+ // Check if tools are registered but have no input validation
48
+ checkInputValidation(files, findings);
49
+ return findings;
50
+ },
51
+ };
52
+ function detectMcpServer(files) {
53
+ for (const file of files) {
54
+ // Check package.json for MCP-related deps
55
+ if (file.relativePath === "package.json") {
56
+ try {
57
+ const pkg = JSON.parse(file.content);
58
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
59
+ if (allDeps["@modelcontextprotocol/sdk"] ||
60
+ allDeps["@anthropic-ai/sdk"] ||
61
+ pkg.mcp) {
62
+ return true;
63
+ }
64
+ }
65
+ catch {
66
+ // ignore parse errors
67
+ }
68
+ }
69
+ // Check for MCP imports in code
70
+ if (file.ext === ".ts" || file.ext === ".js" || file.ext === ".mjs") {
71
+ if (file.content.includes("@modelcontextprotocol/sdk") ||
72
+ file.content.includes("McpServer") ||
73
+ file.content.includes("createMcpServer") ||
74
+ TOOL_REGISTER_RE.test(file.content)) {
75
+ return true;
76
+ }
77
+ }
78
+ // Check for mcp.json or similar config
79
+ if (file.relativePath === "mcp.json" ||
80
+ file.relativePath.endsWith("/mcp.json")) {
81
+ return true;
82
+ }
83
+ }
84
+ return false;
85
+ }
86
+ function checkMcpConfig(files, findings) {
87
+ const mcpConfig = files.find((f) => f.relativePath === "mcp.json" || f.relativePath.endsWith("/mcp.json"));
88
+ if (mcpConfig) {
89
+ try {
90
+ const config = JSON.parse(mcpConfig.content);
91
+ // Check for overly broad permissions
92
+ if (config.permissions) {
93
+ const perms = Array.isArray(config.permissions)
94
+ ? config.permissions
95
+ : Object.keys(config.permissions);
96
+ if (perms.length > 5) {
97
+ findings.push({
98
+ rule: "mcp-manifest",
99
+ severity: "warning",
100
+ file: mcpConfig.relativePath,
101
+ message: `MCP config declares ${perms.length} permissions — consider reducing scope`,
102
+ });
103
+ }
104
+ }
105
+ // Check for wildcard or dangerous permissions
106
+ const configStr = JSON.stringify(config);
107
+ if (configStr.includes('"*"') || configStr.includes('"all"')) {
108
+ findings.push({
109
+ rule: "mcp-manifest",
110
+ severity: "critical",
111
+ file: mcpConfig.relativePath,
112
+ message: "MCP config uses wildcard/all permissions",
113
+ });
114
+ }
115
+ }
116
+ catch {
117
+ findings.push({
118
+ rule: "mcp-manifest",
119
+ severity: "warning",
120
+ file: mcpConfig.relativePath,
121
+ message: "Invalid JSON in MCP config file",
122
+ });
123
+ }
124
+ }
125
+ }
126
+ function checkToolImplementations(files, findings) {
127
+ const codeFiles = files.filter((f) => f.ext === ".ts" || f.ext === ".js" || f.ext === ".mjs" || f.ext === ".cjs");
128
+ for (const file of codeFiles) {
129
+ // Only check files that register tools
130
+ if (!TOOL_REGISTER_RE.test(file.content) && !RESOURCE_REGISTER_RE.test(file.content)) {
131
+ continue;
132
+ }
133
+ for (let i = 0; i < file.lines.length; i++) {
134
+ const line = file.lines[i];
135
+ const trimmed = line.trimStart();
136
+ if (trimmed.startsWith("//") || trimmed.startsWith("*"))
137
+ continue;
138
+ for (const { pattern, desc, severity } of DANGEROUS_TOOL_PATTERNS) {
139
+ if (pattern.test(line)) {
140
+ findings.push({
141
+ rule: "mcp-manifest",
142
+ severity,
143
+ file: file.relativePath,
144
+ line: i + 1,
145
+ message: `MCP tool: ${desc}`,
146
+ evidence: line.trim().slice(0, 120),
147
+ });
148
+ break;
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ function checkToolDescriptions(files, findings) {
155
+ const codeFiles = files.filter((f) => f.ext === ".ts" || f.ext === ".js" || f.ext === ".mjs");
156
+ for (const file of codeFiles) {
157
+ // Look for tool description strings
158
+ for (let i = 0; i < file.lines.length; i++) {
159
+ const line = file.lines[i];
160
+ for (const { pattern, desc } of SUSPICIOUS_TOOL_DESC) {
161
+ if (pattern.test(line)) {
162
+ findings.push({
163
+ rule: "mcp-manifest",
164
+ severity: "warning",
165
+ file: file.relativePath,
166
+ line: i + 1,
167
+ message: `Suspicious MCP tool description: ${desc}`,
168
+ evidence: line.trim().slice(0, 120),
169
+ });
170
+ break;
171
+ }
172
+ }
173
+ }
174
+ }
175
+ }
176
+ function checkInputValidation(files, findings) {
177
+ const codeFiles = files.filter((f) => f.ext === ".ts" || f.ext === ".js" || f.ext === ".mjs");
178
+ for (const file of codeFiles) {
179
+ if (!TOOL_REGISTER_RE.test(file.content))
180
+ continue;
181
+ // Check for tools that accept path inputs without validation
182
+ const hasPathInput = /path|file|dir|folder/i.test(file.content);
183
+ const hasPathValidation = /sanitize|validate|allowlist|whitelist|isAbsolute|normalize|resolve/i.test(file.content);
184
+ const hasTraversalCheck = /\.\.\//i.test(file.content) || /path.*traversal/i.test(file.content);
185
+ if (hasPathInput && !hasPathValidation && !hasTraversalCheck) {
186
+ findings.push({
187
+ rule: "mcp-manifest",
188
+ severity: "warning",
189
+ file: file.relativePath,
190
+ message: "MCP tool accepts path inputs but has no visible path validation/sanitization",
191
+ });
192
+ }
193
+ }
194
+ }
195
+ //# sourceMappingURL=mcp-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-manifest.js","sourceRoot":"","sources":["../../src/rules/mcp-manifest.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import type { ScanResult } from "../types.js";
1
+ import type { ScanResult, ScanConfig } from "../types.js";
2
2
  /** Run all rules against a target directory */
3
- export declare function scan(targetDir: string): ScanResult;
3
+ export declare function scan(targetDir: string, configOverride?: Partial<ScanConfig>): ScanResult;
@@ -1,14 +1,44 @@
1
1
  import { collectFiles, totalLines } from "./files.js";
2
2
  import { rules } from "../rules/index.js";
3
3
  import { computeScore } from "../score.js";
4
+ import { loadConfig, loadIgnorePatterns, isIgnored } from "../config.js";
4
5
  /** Run all rules against a target directory */
5
- export function scan(targetDir) {
6
+ export function scan(targetDir, configOverride) {
6
7
  const start = Date.now();
7
- const files = collectFiles(targetDir);
8
+ // Load config
9
+ const fileConfig = loadConfig(targetDir);
10
+ const config = { ...fileConfig, ...configOverride };
11
+ // Load ignore patterns
12
+ const ignorePatterns = loadIgnorePatterns(targetDir);
13
+ if (config.ignore) {
14
+ ignorePatterns.push(...config.ignore);
15
+ }
16
+ // Collect and filter files
17
+ let files = collectFiles(targetDir);
18
+ if (ignorePatterns.length > 0) {
19
+ files = files.filter((f) => !isIgnored(f.relativePath, ignorePatterns));
20
+ }
21
+ // Filter rules based on config
22
+ let activeRules = [...rules];
23
+ if (config.rules?.enable) {
24
+ activeRules = activeRules.filter((r) => config.rules.enable.includes(r.id));
25
+ }
26
+ if (config.rules?.disable) {
27
+ activeRules = activeRules.filter((r) => !config.rules.disable.includes(r.id));
28
+ }
29
+ // Run rules
8
30
  const findings = [];
9
- for (const rule of rules) {
31
+ for (const rule of activeRules) {
10
32
  findings.push(...rule.run(files));
11
33
  }
34
+ // Apply severity overrides
35
+ if (config.severity) {
36
+ for (const finding of findings) {
37
+ if (config.severity[finding.rule]) {
38
+ finding.severity = config.severity[finding.rule];
39
+ }
40
+ }
41
+ }
12
42
  // Sort: critical first, then warning, then info
13
43
  const severityOrder = { critical: 0, warning: 1, info: 2 };
14
44
  findings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,+CAA+C;AAC/C,MAAM,UAAU,IAAI,CAAC,SAAiB;IACpC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACpC,CAAC;IAED,gDAAgD;IAChD,MAAM,aAAa,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAC3D,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAE/E,OAAO;QACL,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,KAAK,CAAC,MAAM;QAC1B,YAAY,EAAE,UAAU,CAAC,KAAK,CAAC;QAC/B,QAAQ;QACR,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC;QAC7B,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;KAC7B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -40,3 +40,13 @@ export interface SkillMetadata {
40
40
  permissions?: string[];
41
41
  [key: string]: unknown;
42
42
  }
43
+ /** Scan configuration from .agentshield.yml */
44
+ export interface ScanConfig {
45
+ rules?: {
46
+ enable?: string[];
47
+ disable?: string[];
48
+ };
49
+ severity?: Record<string, "critical" | "warning" | "info">;
50
+ failUnder?: number;
51
+ ignore?: string[];
52
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Minimal YAML parser for simple config files.
3
+ * Supports: scalars, lists, nested objects (2 levels deep).
4
+ * For complex YAML, use the 'yaml' package.
5
+ */
6
+ export declare function parse(input: string): Record<string, unknown>;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Minimal YAML parser for simple config files.
3
+ * Supports: scalars, lists, nested objects (2 levels deep).
4
+ * For complex YAML, use the 'yaml' package.
5
+ */
6
+ export function parse(input) {
7
+ const result = {};
8
+ const lines = input.split("\n");
9
+ let currentKey = "";
10
+ let currentSubKey = "";
11
+ let currentList = null;
12
+ for (const rawLine of lines) {
13
+ // Skip comments and empty lines
14
+ const line = rawLine.replace(/#.*$/, "");
15
+ if (!line.trim())
16
+ continue;
17
+ const indent = rawLine.search(/\S/);
18
+ // List item
19
+ if (line.trim().startsWith("- ")) {
20
+ const value = line.trim().slice(2).trim().replace(/^["']|["']$/g, "");
21
+ if (currentList) {
22
+ currentList.push(value);
23
+ }
24
+ continue;
25
+ }
26
+ // Key: value
27
+ const match = line.match(/^(\s*)(\w+)\s*:\s*(.*)/);
28
+ if (!match)
29
+ continue;
30
+ const [, , key, rawValue] = match;
31
+ const value = rawValue.trim().replace(/^["']|["']$/g, "");
32
+ if (indent === 0) {
33
+ // Top-level key
34
+ if (currentList && currentKey) {
35
+ // Save previous list
36
+ if (currentSubKey) {
37
+ result[currentKey][currentSubKey] = currentList;
38
+ }
39
+ else {
40
+ result[currentKey] = currentList;
41
+ }
42
+ currentList = null;
43
+ currentSubKey = "";
44
+ }
45
+ currentKey = key;
46
+ if (value) {
47
+ // Scalar value
48
+ result[currentKey] = parseScalar(value);
49
+ }
50
+ else {
51
+ // Object or list follows
52
+ if (!result[currentKey] || typeof result[currentKey] !== "object") {
53
+ result[currentKey] = {};
54
+ }
55
+ }
56
+ }
57
+ else if (indent === 2 && currentKey) {
58
+ // Sub-key
59
+ if (currentList && currentSubKey) {
60
+ result[currentKey][currentSubKey] = currentList;
61
+ currentList = null;
62
+ }
63
+ currentSubKey = key;
64
+ if (value) {
65
+ if (typeof result[currentKey] !== "object")
66
+ result[currentKey] = {};
67
+ result[currentKey][currentSubKey] = parseScalar(value);
68
+ }
69
+ else {
70
+ // List follows
71
+ currentList = [];
72
+ }
73
+ }
74
+ }
75
+ // Save trailing list
76
+ if (currentList) {
77
+ if (currentSubKey && currentKey) {
78
+ if (typeof result[currentKey] !== "object")
79
+ result[currentKey] = {};
80
+ result[currentKey][currentSubKey] = currentList;
81
+ }
82
+ else if (currentKey) {
83
+ result[currentKey] = currentList;
84
+ }
85
+ }
86
+ return result;
87
+ }
88
+ function parseScalar(value) {
89
+ if (value === "true")
90
+ return true;
91
+ if (value === "false")
92
+ return false;
93
+ const num = Number(value);
94
+ if (!isNaN(num) && value !== "")
95
+ return num;
96
+ return value;
97
+ }
98
+ //# sourceMappingURL=yaml-simple.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml-simple.js","sourceRoot":"","sources":["../src/yaml-simple.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliotllliu/agentshield",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Security scanner for AI agent skills, MCP servers, and plugins",
5
5
  "type": "module",
6
6
  "bin": {