@elliotllliu/agent-shield 0.3.1

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.
Files changed (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +297 -0
  3. package/README.zh-CN.md +130 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +265 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config.d.ts +24 -0
  8. package/dist/config.js +91 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/discover.d.ts +9 -0
  11. package/dist/discover.js +143 -0
  12. package/dist/discover.js.map +1 -0
  13. package/dist/llm/anthropic.d.ts +10 -0
  14. package/dist/llm/anthropic.js +67 -0
  15. package/dist/llm/anthropic.js.map +1 -0
  16. package/dist/llm/index.d.ts +10 -0
  17. package/dist/llm/index.js +41 -0
  18. package/dist/llm/index.js.map +1 -0
  19. package/dist/llm/ollama.d.ts +9 -0
  20. package/dist/llm/ollama.js +61 -0
  21. package/dist/llm/ollama.js.map +1 -0
  22. package/dist/llm/openai.d.ts +10 -0
  23. package/dist/llm/openai.js +66 -0
  24. package/dist/llm/openai.js.map +1 -0
  25. package/dist/llm/prompt.d.ts +3 -0
  26. package/dist/llm/prompt.js +31 -0
  27. package/dist/llm/prompt.js.map +1 -0
  28. package/dist/llm/types.d.ts +23 -0
  29. package/dist/llm/types.js +3 -0
  30. package/dist/llm/types.js.map +1 -0
  31. package/dist/llm-analyzer.d.ts +13 -0
  32. package/dist/llm-analyzer.js +169 -0
  33. package/dist/llm-analyzer.js.map +1 -0
  34. package/dist/reporter/badge.d.ts +7 -0
  35. package/dist/reporter/badge.js +50 -0
  36. package/dist/reporter/badge.js.map +1 -0
  37. package/dist/reporter/json.d.ts +3 -0
  38. package/dist/reporter/json.js +5 -0
  39. package/dist/reporter/json.js.map +1 -0
  40. package/dist/reporter/terminal.d.ts +2 -0
  41. package/dist/reporter/terminal.js +64 -0
  42. package/dist/reporter/terminal.js.map +1 -0
  43. package/dist/rules/backdoor.d.ts +2 -0
  44. package/dist/rules/backdoor.js +57 -0
  45. package/dist/rules/backdoor.js.map +1 -0
  46. package/dist/rules/credential-hardcode.d.ts +2 -0
  47. package/dist/rules/credential-hardcode.js +57 -0
  48. package/dist/rules/credential-hardcode.js.map +1 -0
  49. package/dist/rules/crypto-mining.d.ts +2 -0
  50. package/dist/rules/crypto-mining.js +41 -0
  51. package/dist/rules/crypto-mining.js.map +1 -0
  52. package/dist/rules/data-exfil.d.ts +2 -0
  53. package/dist/rules/data-exfil.js +61 -0
  54. package/dist/rules/data-exfil.js.map +1 -0
  55. package/dist/rules/env-leak.d.ts +2 -0
  56. package/dist/rules/env-leak.js +43 -0
  57. package/dist/rules/env-leak.js.map +1 -0
  58. package/dist/rules/excessive-perms.d.ts +2 -0
  59. package/dist/rules/excessive-perms.js +50 -0
  60. package/dist/rules/excessive-perms.js.map +1 -0
  61. package/dist/rules/hidden-files.d.ts +2 -0
  62. package/dist/rules/hidden-files.js +52 -0
  63. package/dist/rules/hidden-files.js.map +1 -0
  64. package/dist/rules/index.d.ts +5 -0
  65. package/dist/rules/index.js +53 -0
  66. package/dist/rules/index.js.map +1 -0
  67. package/dist/rules/mcp-manifest.d.ts +2 -0
  68. package/dist/rules/mcp-manifest.js +270 -0
  69. package/dist/rules/mcp-manifest.js.map +1 -0
  70. package/dist/rules/network-ssrf.d.ts +2 -0
  71. package/dist/rules/network-ssrf.js +51 -0
  72. package/dist/rules/network-ssrf.js.map +1 -0
  73. package/dist/rules/obfuscation.d.ts +2 -0
  74. package/dist/rules/obfuscation.js +51 -0
  75. package/dist/rules/obfuscation.js.map +1 -0
  76. package/dist/rules/phone-home.d.ts +2 -0
  77. package/dist/rules/phone-home.js +38 -0
  78. package/dist/rules/phone-home.js.map +1 -0
  79. package/dist/rules/privilege.d.ts +2 -0
  80. package/dist/rules/privilege.js +111 -0
  81. package/dist/rules/privilege.js.map +1 -0
  82. package/dist/rules/prompt-injection.d.ts +2 -0
  83. package/dist/rules/prompt-injection.js +323 -0
  84. package/dist/rules/prompt-injection.js.map +1 -0
  85. package/dist/rules/reverse-shell.d.ts +2 -0
  86. package/dist/rules/reverse-shell.js +53 -0
  87. package/dist/rules/reverse-shell.js.map +1 -0
  88. package/dist/rules/sensitive-read.d.ts +2 -0
  89. package/dist/rules/sensitive-read.js +53 -0
  90. package/dist/rules/sensitive-read.js.map +1 -0
  91. package/dist/rules/skill-risks.d.ts +2 -0
  92. package/dist/rules/skill-risks.js +148 -0
  93. package/dist/rules/skill-risks.js.map +1 -0
  94. package/dist/rules/supply-chain.d.ts +6 -0
  95. package/dist/rules/supply-chain.js +105 -0
  96. package/dist/rules/supply-chain.js.map +1 -0
  97. package/dist/rules/tool-shadowing.d.ts +2 -0
  98. package/dist/rules/tool-shadowing.js +129 -0
  99. package/dist/rules/tool-shadowing.js.map +1 -0
  100. package/dist/rules/toxic-flow.d.ts +2 -0
  101. package/dist/rules/toxic-flow.js +160 -0
  102. package/dist/rules/toxic-flow.js.map +1 -0
  103. package/dist/rules/typosquatting.d.ts +2 -0
  104. package/dist/rules/typosquatting.js +56 -0
  105. package/dist/rules/typosquatting.js.map +1 -0
  106. package/dist/scanner/files.d.ts +5 -0
  107. package/dist/scanner/files.js +105 -0
  108. package/dist/scanner/files.js.map +1 -0
  109. package/dist/scanner/index.d.ts +6 -0
  110. package/dist/scanner/index.js +198 -0
  111. package/dist/scanner/index.js.map +1 -0
  112. package/dist/score.d.ts +14 -0
  113. package/dist/score.js +35 -0
  114. package/dist/score.js.map +1 -0
  115. package/dist/types.d.ts +60 -0
  116. package/dist/types.js +2 -0
  117. package/dist/types.js.map +1 -0
  118. package/dist/yaml-simple.d.ts +6 -0
  119. package/dist/yaml-simple.js +98 -0
  120. package/dist/yaml-simple.js.map +1 -0
  121. package/package.json +72 -0
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Rule: typosquatting
3
+ * Detects potentially typosquatted npm package names.
4
+ */
5
+ // Known popular packages and their common typos
6
+ const TYPOSQUAT_MAP = {
7
+ lodash: ["1odash", "lodsh", "lodashs", "lodahs"],
8
+ express: ["expresss", "expres", "exress", "exppress"],
9
+ axios: ["axois", "axio", "axioss", "axiso"],
10
+ react: ["raect", "reacct", "reactt"],
11
+ chalk: ["chalks", "chalkk", "chak"],
12
+ commander: ["comander", "commanderr", "commmander"],
13
+ "node-fetch": ["node-ftch", "nodefetch", "node-fetchh"],
14
+ request: ["reqeust", "requets", "reuqest"],
15
+ mongoose: ["mongose", "mongosse", "mongooose"],
16
+ webpack: ["webpck", "webpackk", "weback"],
17
+ eslint: ["eslintt", "eslnt", "elint"],
18
+ typescript: ["typscript", "typescipt", "typesript"],
19
+ };
20
+ export const typosquattingRule = {
21
+ id: "typosquatting",
22
+ name: "Dependency Typosquatting",
23
+ description: "Detects potentially typosquatted package names in dependencies",
24
+ run(files) {
25
+ const findings = [];
26
+ const pkgJson = files.find((f) => f.relativePath === "package.json" || f.relativePath.endsWith("/package.json"));
27
+ if (!pkgJson)
28
+ return findings;
29
+ try {
30
+ const pkg = JSON.parse(pkgJson.content);
31
+ const allDeps = {
32
+ ...pkg.dependencies,
33
+ ...pkg.devDependencies,
34
+ ...pkg.peerDependencies,
35
+ ...pkg.optionalDependencies,
36
+ };
37
+ for (const depName of Object.keys(allDeps)) {
38
+ for (const [legitimate, typos] of Object.entries(TYPOSQUAT_MAP)) {
39
+ if (typos.includes(depName.toLowerCase())) {
40
+ findings.push({
41
+ rule: "typosquatting",
42
+ severity: "critical",
43
+ file: pkgJson.relativePath,
44
+ message: `Suspicious package "${depName}" — possible typosquat of "${legitimate}"`,
45
+ });
46
+ }
47
+ }
48
+ }
49
+ }
50
+ catch {
51
+ // ignore parse errors
52
+ }
53
+ return findings;
54
+ },
55
+ };
56
+ //# sourceMappingURL=typosquatting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typosquatting.js","sourceRoot":"","sources":["../../src/rules/typosquatting.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,gDAAgD;AAChD,MAAM,aAAa,GAA6B;IAC9C,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC;IAChD,OAAO,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC;IACrD,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC;IAC3C,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;IACpC,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC;IACnC,SAAS,EAAE,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,CAAC;IACnD,YAAY,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,aAAa,CAAC;IACvD,OAAO,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;IAC1C,QAAQ,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,WAAW,CAAC;IAC9C,OAAO,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC;IACzC,MAAM,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC;IACrC,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,CAAC;CACpD,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAS;IACrC,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,0BAA0B;IAChC,WAAW,EAAE,gEAAgE;IAE7E,GAAG,CAAC,KAAoB;QACtB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CACxB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,cAAc,IAAI,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,eAAe,CAAC,CACrF,CAAC;QACF,IAAI,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC;QAE9B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACxC,MAAM,OAAO,GAAG;gBACd,GAAG,GAAG,CAAC,YAAY;gBACnB,GAAG,GAAG,CAAC,eAAe;gBACtB,GAAG,GAAG,CAAC,gBAAgB;gBACvB,GAAG,GAAG,CAAC,oBAAoB;aAC5B,CAAC;YAEF,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC3C,KAAK,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;oBAChE,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;wBAC1C,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,eAAe;4BACrB,QAAQ,EAAE,UAAU;4BACpB,IAAI,EAAE,OAAO,CAAC,YAAY;4BAC1B,OAAO,EAAE,uBAAuB,OAAO,8BAA8B,UAAU,GAAG;yBACnF,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ScannedFile } from "../types.js";
2
+ /** Recursively collect scannable files from a directory */
3
+ export declare function collectFiles(dir: string, base?: string): ScannedFile[];
4
+ /** Count total lines across files */
5
+ export declare function totalLines(files: ScannedFile[]): number;
@@ -0,0 +1,105 @@
1
+ import { readFileSync, statSync, readdirSync } from "fs";
2
+ import { join, relative, extname, dirname } from "path";
3
+ const SKIP_DIRS = new Set([
4
+ "node_modules", ".git", "dist", "build", "__pycache__", ".venv", "venv",
5
+ ]);
6
+ const CODE_EXTS = new Set([
7
+ ".ts", ".js", ".mjs", ".cjs", ".tsx", ".jsx",
8
+ ".py", ".sh", ".bash", ".zsh",
9
+ ".json", ".yaml", ".yml", ".toml",
10
+ ".md",
11
+ ]);
12
+ const MAX_FILE_SIZE = 512 * 1024; // 512 KB
13
+ /** Recursively collect scannable files from a directory */
14
+ export function collectFiles(dir, base) {
15
+ const root = base ?? dir;
16
+ const files = [];
17
+ let entries;
18
+ try {
19
+ entries = readdirSync(dir);
20
+ }
21
+ catch {
22
+ return files;
23
+ }
24
+ for (const name of entries) {
25
+ if (name.startsWith(".") && name !== ".env")
26
+ continue;
27
+ if (SKIP_DIRS.has(name))
28
+ continue;
29
+ const fullPath = join(dir, name);
30
+ let stat;
31
+ try {
32
+ stat = statSync(fullPath);
33
+ }
34
+ catch {
35
+ continue;
36
+ }
37
+ if (stat.isDirectory()) {
38
+ files.push(...collectFiles(fullPath, root));
39
+ }
40
+ else if (stat.isFile()) {
41
+ const ext = extname(name).toLowerCase();
42
+ if (!CODE_EXTS.has(ext) && name !== "SKILL.md")
43
+ continue;
44
+ if (stat.size > MAX_FILE_SIZE)
45
+ continue;
46
+ try {
47
+ const content = readFileSync(fullPath, "utf-8");
48
+ const relPath = relative(root, fullPath);
49
+ files.push({
50
+ path: fullPath,
51
+ relativePath: relPath,
52
+ content,
53
+ lines: content.split("\n"),
54
+ ext,
55
+ context: detectFileContext(relPath, name),
56
+ });
57
+ }
58
+ catch {
59
+ // skip unreadable files
60
+ }
61
+ }
62
+ }
63
+ return files;
64
+ }
65
+ /** Count total lines across files */
66
+ export function totalLines(files) {
67
+ return files.reduce((sum, f) => sum + f.lines.length, 0);
68
+ }
69
+ /** Detect file context for false positive reduction */
70
+ function detectFileContext(relativePath, fileName) {
71
+ const lowerPath = relativePath.toLowerCase();
72
+ const lowerName = fileName.toLowerCase();
73
+ const dirName = dirname(lowerPath).toLowerCase();
74
+ // Test files
75
+ if (lowerName.includes(".test.") || lowerName.includes(".spec.") ||
76
+ lowerName.includes("_test.") || lowerName.includes("_spec.") ||
77
+ lowerPath.includes("__tests__") || lowerPath.includes("/tests/") ||
78
+ lowerPath.startsWith("tests/") || lowerPath.startsWith("test/") ||
79
+ lowerName === "jest.config.js" || lowerName === "vitest.config.ts") {
80
+ return "test";
81
+ }
82
+ // Deploy / CI scripts
83
+ if (lowerPath.includes("deploy") || lowerPath.includes("ci/") ||
84
+ lowerPath.includes(".github/") || lowerPath.includes("scripts/") ||
85
+ lowerPath.includes("infra/") || lowerPath.includes("ops/") ||
86
+ lowerName.includes("deploy") || lowerName.includes("release") ||
87
+ lowerName === "dockerfile" || lowerName === "makefile") {
88
+ return "deploy";
89
+ }
90
+ // Config files
91
+ if ([".json", ".yaml", ".yml", ".toml"].includes(extname(lowerName)) &&
92
+ !lowerName.includes("skill")) {
93
+ return "config";
94
+ }
95
+ // Documentation
96
+ if (extname(lowerName) === ".md") {
97
+ return "docs";
98
+ }
99
+ // Shell scripts (standalone)
100
+ if ([".sh", ".bash", ".zsh"].includes(extname(lowerName))) {
101
+ return "script";
102
+ }
103
+ return "source";
104
+ }
105
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/scanner/files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { ScanResult, ScanConfig } from "../types.js";
2
+ import type { LlmProvider } from "../llm/types.js";
3
+ /** Run all rules against a target directory */
4
+ export declare function scan(targetDir: string, configOverride?: Partial<ScanConfig>): ScanResult;
5
+ /** Run all rules + optional LLM deep analysis */
6
+ export declare function scanWithLlm(targetDir: string, llmProvider: LlmProvider, configOverride?: Partial<ScanConfig>): Promise<ScanResult>;
@@ -0,0 +1,198 @@
1
+ import { collectFiles, totalLines } from "./files.js";
2
+ import { rules } from "../rules/index.js";
3
+ import { computeScore } from "../score.js";
4
+ import { loadConfig, loadIgnorePatterns, isIgnored } from "../config.js";
5
+ /** Context types where findings are likely false positives */
6
+ const FP_CONTEXTS = {
7
+ test: "Test file — assertions and mocks commonly trigger security patterns",
8
+ deploy: "Deploy/CI script — HTTP requests and credential access are expected",
9
+ docs: "Documentation file — code examples and descriptions commonly trigger patterns",
10
+ config: "Config file — expected to contain URLs, paths, and credential references",
11
+ };
12
+ /** Rules most likely to false-positive in specific contexts */
13
+ const CONTEXT_FP_RULES = {
14
+ test: new Set(["data-exfil", "env-leak", "backdoor", "network-ssrf", "sensitive-read", "phone-home", "obfuscation", "crypto-mining", "reverse-shell", "credential-hardcode", "skill-risks"]),
15
+ deploy: new Set(["data-exfil", "env-leak", "backdoor", "network-ssrf", "credential-hardcode", "sensitive-read", "skill-risks"]),
16
+ docs: new Set(["data-exfil", "env-leak", "backdoor", "network-ssrf", "sensitive-read", "obfuscation", "crypto-mining", "reverse-shell", "credential-hardcode", "skill-risks"]),
17
+ config: new Set(["data-exfil", "env-leak", "credential-hardcode", "network-ssrf"]),
18
+ };
19
+ /** Run all rules against a target directory */
20
+ export function scan(targetDir, configOverride) {
21
+ const start = Date.now();
22
+ // Load config
23
+ const fileConfig = loadConfig(targetDir);
24
+ const config = { ...fileConfig, ...configOverride };
25
+ // Load ignore patterns
26
+ const ignorePatterns = loadIgnorePatterns(targetDir);
27
+ if (config.ignore) {
28
+ ignorePatterns.push(...config.ignore);
29
+ }
30
+ // Collect and filter files
31
+ let files = collectFiles(targetDir);
32
+ if (ignorePatterns.length > 0) {
33
+ files = files.filter((f) => !isIgnored(f.relativePath, ignorePatterns));
34
+ }
35
+ // Filter rules based on config
36
+ let activeRules = [...rules];
37
+ if (config.rules?.enable) {
38
+ activeRules = activeRules.filter((r) => config.rules.enable.includes(r.id));
39
+ }
40
+ if (config.rules?.disable) {
41
+ activeRules = activeRules.filter((r) => !config.rules.disable.includes(r.id));
42
+ }
43
+ // Run rules
44
+ const findings = [];
45
+ for (const rule of activeRules) {
46
+ findings.push(...rule.run(files));
47
+ }
48
+ // Post-process: false positive detection, severity overrides, sorting
49
+ postProcess(findings, files, config);
50
+ return {
51
+ target: targetDir,
52
+ filesScanned: files.length,
53
+ linesScanned: totalLines(files),
54
+ findings,
55
+ score: computeScore(findings),
56
+ duration: Date.now() - start,
57
+ };
58
+ }
59
+ /** Common post-processing: false positive detection, severity overrides, sorting */
60
+ function postProcess(findings, files, config) {
61
+ for (const finding of findings) {
62
+ const file = files.find((f) => f.relativePath === finding.file || f.path === finding.file);
63
+ // Context-based FP detection
64
+ if (file && FP_CONTEXTS[file.context]) {
65
+ const fpRules = CONTEXT_FP_RULES[file.context];
66
+ if (fpRules?.has(finding.rule)) {
67
+ finding.possibleFalsePositive = true;
68
+ finding.falsePositiveReason = FP_CONTEXTS[file.context];
69
+ if (finding.severity === "critical") {
70
+ finding.severity = "warning";
71
+ }
72
+ else if (finding.severity === "warning") {
73
+ finding.severity = "info";
74
+ }
75
+ }
76
+ }
77
+ // Security tool self-reference detection:
78
+ // If a file is a rule definition (contains regex patterns as data),
79
+ // its matches on code-analysis rules are likely false positives
80
+ if (file) {
81
+ const isRuleFile = file.relativePath.includes("rules/") && file.ext === ".ts";
82
+ const isSecurityToolCode = file.relativePath.includes("llm/") ||
83
+ file.relativePath.includes("llm-analyzer") ||
84
+ file.relativePath.includes("scanner/") ||
85
+ file.relativePath.includes("reporter/") ||
86
+ file.relativePath.includes("score.");
87
+ if (isRuleFile || isSecurityToolCode) {
88
+ const codeAnalysisRules = new Set([
89
+ "data-exfil", "backdoor", "obfuscation", "env-leak",
90
+ "crypto-mining", "reverse-shell", "sensitive-read",
91
+ "network-ssrf", "credential-hardcode", "mcp-manifest",
92
+ "skill-risks",
93
+ ]);
94
+ if (codeAnalysisRules.has(finding.rule)) {
95
+ finding.possibleFalsePositive = true;
96
+ finding.falsePositiveReason = "Security tool source code — pattern definitions are not actual vulnerabilities";
97
+ if (finding.severity === "critical") {
98
+ finding.severity = "info";
99
+ }
100
+ else if (finding.severity === "warning") {
101
+ finding.severity = "info";
102
+ }
103
+ }
104
+ }
105
+ }
106
+ // Evidence-based FP: regex pattern strings (common in security tools)
107
+ if (finding.evidence) {
108
+ const isRegexDef = /(?:pattern|RegExp|\/[^/]+\/[gimsuy]*|new RegExp)\s*[:=(]/.test(finding.evidence);
109
+ const isStringDef = /(?:const|let|var)\s+\w+\s*=\s*["'`]/.test(finding.evidence);
110
+ if (isRegexDef && !finding.possibleFalsePositive) {
111
+ finding.possibleFalsePositive = true;
112
+ finding.falsePositiveReason = "Pattern definition — regex/string constant, not executable code";
113
+ if (finding.severity !== "info")
114
+ finding.severity = "info";
115
+ }
116
+ }
117
+ }
118
+ if (config.severity) {
119
+ for (const finding of findings) {
120
+ if (config.severity[finding.rule]) {
121
+ finding.severity = config.severity[finding.rule];
122
+ }
123
+ }
124
+ }
125
+ const severityOrder = { critical: 0, warning: 1, info: 2 };
126
+ findings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
127
+ }
128
+ /** LLM-capable file extensions for deep analysis */
129
+ const LLM_SCAN_EXTS = new Set([".md", ".json", ".yaml", ".yml", ".py"]);
130
+ /** Run all rules + optional LLM deep analysis */
131
+ export async function scanWithLlm(targetDir, llmProvider, configOverride) {
132
+ const start = Date.now();
133
+ const fileConfig = loadConfig(targetDir);
134
+ const config = { ...fileConfig, ...configOverride };
135
+ const ignorePatterns = loadIgnorePatterns(targetDir);
136
+ if (config.ignore)
137
+ ignorePatterns.push(...config.ignore);
138
+ let files = collectFiles(targetDir);
139
+ if (ignorePatterns.length > 0) {
140
+ files = files.filter((f) => !isIgnored(f.relativePath, ignorePatterns));
141
+ }
142
+ let activeRules = [...rules];
143
+ if (config.rules?.enable) {
144
+ activeRules = activeRules.filter((r) => config.rules.enable.includes(r.id));
145
+ }
146
+ if (config.rules?.disable) {
147
+ activeRules = activeRules.filter((r) => !config.rules.disable.includes(r.id));
148
+ }
149
+ // Phase 1: static regex rules
150
+ const findings = [];
151
+ for (const rule of activeRules) {
152
+ findings.push(...rule.run(files));
153
+ }
154
+ // Phase 2: LLM deep analysis on markdown, config, and Python files
155
+ const llmTargets = files.filter((f) => LLM_SCAN_EXTS.has(f.ext));
156
+ let totalTokens = 0;
157
+ for (const file of llmTargets) {
158
+ try {
159
+ const result = await llmProvider.analyze(file.content, file.relativePath);
160
+ totalTokens += result.tokensUsed || 0;
161
+ for (const llmFinding of result.findings) {
162
+ // Deduplicate: skip if regex already found a similar issue on the same line
163
+ const isDuplicate = findings.some((f) => f.file === file.relativePath &&
164
+ f.line === llmFinding.line &&
165
+ f.rule === "prompt-injection");
166
+ if (!isDuplicate) {
167
+ findings.push({
168
+ rule: "prompt-injection-llm",
169
+ severity: llmFinding.severity,
170
+ file: file.relativePath,
171
+ line: llmFinding.line,
172
+ message: `[LLM] ${llmFinding.description}`,
173
+ evidence: llmFinding.evidence,
174
+ });
175
+ }
176
+ }
177
+ }
178
+ catch (err) {
179
+ // LLM failure is non-fatal — fallback to regex-only
180
+ findings.push({
181
+ rule: "prompt-injection-llm",
182
+ severity: "info",
183
+ file: file.relativePath,
184
+ message: `LLM analysis skipped: ${err.message?.slice(0, 80) || "unknown error"}`,
185
+ });
186
+ }
187
+ }
188
+ postProcess(findings, files, config);
189
+ return {
190
+ target: targetDir,
191
+ filesScanned: files.length,
192
+ linesScanned: totalLines(files),
193
+ findings,
194
+ score: computeScore(findings),
195
+ duration: Date.now() - start,
196
+ };
197
+ }
198
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { Finding } from "./types.js";
2
+ /**
3
+ * Compute a security score from 0-100.
4
+ *
5
+ * Starts at 100, deducts points per finding:
6
+ * critical: -25
7
+ * warning: -10
8
+ * info: -0
9
+ *
10
+ * Minimum score is 0.
11
+ */
12
+ export declare function computeScore(findings: Finding[]): number;
13
+ /** Human-readable risk label */
14
+ export declare function riskLabel(score: number): string;
package/dist/score.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Compute a security score from 0-100.
3
+ *
4
+ * Starts at 100, deducts points per finding:
5
+ * critical: -25
6
+ * warning: -10
7
+ * info: -0
8
+ *
9
+ * Minimum score is 0.
10
+ */
11
+ export function computeScore(findings) {
12
+ let score = 100;
13
+ for (const f of findings) {
14
+ switch (f.severity) {
15
+ case "critical":
16
+ score -= 25;
17
+ break;
18
+ case "warning":
19
+ score -= 10;
20
+ break;
21
+ }
22
+ }
23
+ return Math.max(0, score);
24
+ }
25
+ /** Human-readable risk label */
26
+ export function riskLabel(score) {
27
+ if (score >= 90)
28
+ return "Low Risk";
29
+ if (score >= 70)
30
+ return "Moderate Risk";
31
+ if (score >= 40)
32
+ return "High Risk";
33
+ return "Critical Risk";
34
+ }
35
+ //# sourceMappingURL=score.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score.js","sourceRoot":"","sources":["../src/score.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,QAAmB;IAC9C,IAAI,KAAK,GAAG,GAAG,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;YACnB,KAAK,UAAU;gBACb,KAAK,IAAI,EAAE,CAAC;gBACZ,MAAM;YACR,KAAK,SAAS;gBACZ,KAAK,IAAI,EAAE,CAAC;gBACZ,MAAM;QACV,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,UAAU,CAAC;IACnC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,eAAe,CAAC;IACxC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,WAAW,CAAC;IACpC,OAAO,eAAe,CAAC;AACzB,CAAC"}
@@ -0,0 +1,60 @@
1
+ /** Severity levels for findings */
2
+ export type Severity = "critical" | "warning" | "info";
3
+ /** File context hints for reducing false positives */
4
+ export type FileContext = "test" | "deploy" | "config" | "docs" | "script" | "source";
5
+ /** A single security finding */
6
+ export interface Finding {
7
+ rule: string;
8
+ severity: Severity;
9
+ file: string;
10
+ line?: number;
11
+ message: string;
12
+ evidence?: string;
13
+ /** If true, the finding is likely a false positive due to file context */
14
+ possibleFalsePositive?: boolean;
15
+ /** Why it might be a false positive */
16
+ falsePositiveReason?: string;
17
+ }
18
+ /** Scan result for a directory */
19
+ export interface ScanResult {
20
+ target: string;
21
+ filesScanned: number;
22
+ linesScanned: number;
23
+ findings: Finding[];
24
+ score: number;
25
+ duration: number;
26
+ }
27
+ /** A scanner rule */
28
+ export interface Rule {
29
+ id: string;
30
+ name: string;
31
+ description: string;
32
+ run(files: ScannedFile[]): Finding[];
33
+ }
34
+ /** A file loaded for scanning */
35
+ export interface ScannedFile {
36
+ path: string;
37
+ relativePath: string;
38
+ content: string;
39
+ lines: string[];
40
+ ext: string;
41
+ /** Detected file context for false positive reduction */
42
+ context: FileContext;
43
+ }
44
+ /** Parsed SKILL.md metadata */
45
+ export interface SkillMetadata {
46
+ name?: string;
47
+ description?: string;
48
+ permissions?: string[];
49
+ [key: string]: unknown;
50
+ }
51
+ /** Scan configuration from .agentshield.yml */
52
+ export interface ScanConfig {
53
+ rules?: {
54
+ enable?: string[];
55
+ disable?: string[];
56
+ };
57
+ severity?: Record<string, "critical" | "warning" | "info">;
58
+ failUnder?: number;
59
+ ignore?: string[];
60
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -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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"}