@sigildev/sigil 0.1.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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +246 -0
  3. package/dist/analyzers/ast/python.d.ts +14 -0
  4. package/dist/analyzers/ast/python.d.ts.map +1 -0
  5. package/dist/analyzers/ast/python.js +15 -0
  6. package/dist/analyzers/ast/python.js.map +1 -0
  7. package/dist/analyzers/ast/taint.d.ts +45 -0
  8. package/dist/analyzers/ast/taint.d.ts.map +1 -0
  9. package/dist/analyzers/ast/taint.js +32 -0
  10. package/dist/analyzers/ast/taint.js.map +1 -0
  11. package/dist/analyzers/ast/typescript.d.ts +15 -0
  12. package/dist/analyzers/ast/typescript.d.ts.map +1 -0
  13. package/dist/analyzers/ast/typescript.js +16 -0
  14. package/dist/analyzers/ast/typescript.js.map +1 -0
  15. package/dist/analyzers/deps.d.ts +13 -0
  16. package/dist/analyzers/deps.d.ts.map +1 -0
  17. package/dist/analyzers/deps.js +14 -0
  18. package/dist/analyzers/deps.js.map +1 -0
  19. package/dist/analyzers/pattern.d.ts +12 -0
  20. package/dist/analyzers/pattern.d.ts.map +1 -0
  21. package/dist/analyzers/pattern.js +13 -0
  22. package/dist/analyzers/pattern.js.map +1 -0
  23. package/dist/analyzers/types.d.ts +111 -0
  24. package/dist/analyzers/types.d.ts.map +1 -0
  25. package/dist/analyzers/types.js +3 -0
  26. package/dist/analyzers/types.js.map +1 -0
  27. package/dist/discovery/config-parser.d.ts +7 -0
  28. package/dist/discovery/config-parser.d.ts.map +1 -0
  29. package/dist/discovery/config-parser.js +23 -0
  30. package/dist/discovery/config-parser.js.map +1 -0
  31. package/dist/discovery/files.d.ts +6 -0
  32. package/dist/discovery/files.d.ts.map +1 -0
  33. package/dist/discovery/files.js +43 -0
  34. package/dist/discovery/files.js.map +1 -0
  35. package/dist/discovery/manifest.d.ts +6 -0
  36. package/dist/discovery/manifest.d.ts.map +1 -0
  37. package/dist/discovery/manifest.js +82 -0
  38. package/dist/discovery/manifest.js.map +1 -0
  39. package/dist/index.d.ts +3 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +60 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/reporters/json.d.ts +3 -0
  44. package/dist/reporters/json.d.ts.map +1 -0
  45. package/dist/reporters/json.js +4 -0
  46. package/dist/reporters/json.js.map +1 -0
  47. package/dist/reporters/sarif.d.ts +3 -0
  48. package/dist/reporters/sarif.d.ts.map +1 -0
  49. package/dist/reporters/sarif.js +57 -0
  50. package/dist/reporters/sarif.js.map +1 -0
  51. package/dist/reporters/text.d.ts +7 -0
  52. package/dist/reporters/text.d.ts.map +1 -0
  53. package/dist/reporters/text.js +89 -0
  54. package/dist/reporters/text.js.map +1 -0
  55. package/dist/rules/auth.d.ts +4 -0
  56. package/dist/rules/auth.d.ts.map +1 -0
  57. package/dist/rules/auth.js +88 -0
  58. package/dist/rules/auth.js.map +1 -0
  59. package/dist/rules/config.d.ts +5 -0
  60. package/dist/rules/config.d.ts.map +1 -0
  61. package/dist/rules/config.js +123 -0
  62. package/dist/rules/config.js.map +1 -0
  63. package/dist/rules/data.d.ts +4 -0
  64. package/dist/rules/data.d.ts.map +1 -0
  65. package/dist/rules/data.js +79 -0
  66. package/dist/rules/data.js.map +1 -0
  67. package/dist/rules/deps.d.ts +3 -0
  68. package/dist/rules/deps.d.ts.map +1 -0
  69. package/dist/rules/deps.js +68 -0
  70. package/dist/rules/deps.js.map +1 -0
  71. package/dist/rules/description.d.ts +3 -0
  72. package/dist/rules/description.d.ts.map +1 -0
  73. package/dist/rules/description.js +91 -0
  74. package/dist/rules/description.js.map +1 -0
  75. package/dist/rules/index.d.ts +3 -0
  76. package/dist/rules/index.d.ts.map +1 -0
  77. package/dist/rules/index.js +154 -0
  78. package/dist/rules/index.js.map +1 -0
  79. package/dist/rules/injection.d.ts +5 -0
  80. package/dist/rules/injection.d.ts.map +1 -0
  81. package/dist/rules/injection.js +213 -0
  82. package/dist/rules/injection.js.map +1 -0
  83. package/dist/rules/permissions.d.ts +5 -0
  84. package/dist/rules/permissions.d.ts.map +1 -0
  85. package/dist/rules/permissions.js +170 -0
  86. package/dist/rules/permissions.js.map +1 -0
  87. package/dist/rules/validation.d.ts +3 -0
  88. package/dist/rules/validation.d.ts.map +1 -0
  89. package/dist/rules/validation.js +67 -0
  90. package/dist/rules/validation.js.map +1 -0
  91. package/dist/scanner.d.ts +9 -0
  92. package/dist/scanner.d.ts.map +1 -0
  93. package/dist/scanner.js +149 -0
  94. package/dist/scanner.js.map +1 -0
  95. package/dist/scoring.d.ts +3 -0
  96. package/dist/scoring.d.ts.map +1 -0
  97. package/dist/scoring.js +35 -0
  98. package/dist/scoring.js.map +1 -0
  99. package/package.json +57 -0
@@ -0,0 +1,123 @@
1
+ function findLineNumber(content, index) {
2
+ return content.slice(0, index).split("\n").length;
3
+ }
4
+ export function detectDebugMode(context) {
5
+ const findings = [];
6
+ const DEBUG_PATTERNS = [
7
+ /\bDEBUG\s*[:=]\s*(?:true|1|["']true["'])/gi,
8
+ /\bdebug\s*[:=]\s*true/g,
9
+ /NODE_ENV\s*[:=!]=?\s*["']development["']/g,
10
+ /\.use\s*\(\s*\w*[Dd]ebug/g,
11
+ /logging\.DEBUG/g,
12
+ /log_level\s*=\s*["']debug["']/gi,
13
+ ];
14
+ for (const [file, content] of context.sources) {
15
+ for (const pattern of DEBUG_PATTERNS) {
16
+ pattern.lastIndex = 0;
17
+ let match;
18
+ while ((match = pattern.exec(content)) !== null) {
19
+ const line = findLineNumber(content, match.index);
20
+ const lineContent = content.split("\n")[line - 1] || "";
21
+ const trimmed = lineContent.trimStart();
22
+ if (trimmed.startsWith("//") || trimmed.startsWith("#") || trimmed.startsWith("*"))
23
+ continue;
24
+ // Skip if it's in a conditional check (e.g., if (NODE_ENV === 'development'))
25
+ if (/if\s*\(/.test(lineContent) || /if\s+/.test(lineContent))
26
+ continue;
27
+ findings.push({
28
+ ruleId: "MCS-CFG-001",
29
+ severity: "medium",
30
+ title: "Debug Mode Enabled",
31
+ message: `Debug or development configuration appears to be enabled. Ensure this is disabled in production.`,
32
+ location: { file, startLine: line, endLine: line },
33
+ fix: {
34
+ description: "Use environment-based configuration to disable debug mode in production.",
35
+ },
36
+ });
37
+ }
38
+ }
39
+ }
40
+ return findings;
41
+ }
42
+ export function detectVerboseErrors(context) {
43
+ const findings = [];
44
+ const VERBOSE_PATTERNS_TS = [
45
+ /\.stack\b/g,
46
+ /error\.message\b/g,
47
+ /JSON\.stringify\s*\(\s*(?:err|error)\b/g,
48
+ /console\.error\s*\(\s*(?:err|error)\b/g,
49
+ ];
50
+ const VERBOSE_PATTERNS_PY = [
51
+ /traceback\.format_exc/g,
52
+ /traceback\.print_exc/g,
53
+ /str\s*\(\s*(?:e|err|error|exception)\s*\)/g,
54
+ ];
55
+ const patterns = context.language === "python" ? VERBOSE_PATTERNS_PY :
56
+ context.language === "typescript" ? VERBOSE_PATTERNS_TS :
57
+ [...VERBOSE_PATTERNS_TS, ...VERBOSE_PATTERNS_PY];
58
+ for (const [file, content] of context.sources) {
59
+ for (const pattern of patterns) {
60
+ pattern.lastIndex = 0;
61
+ let match;
62
+ while ((match = pattern.exec(content)) !== null) {
63
+ const line = findLineNumber(content, match.index);
64
+ const lineContent = content.split("\n")[line - 1] || "";
65
+ const trimmed = lineContent.trimStart();
66
+ if (trimmed.startsWith("//") || trimmed.startsWith("#") || trimmed.startsWith("*"))
67
+ continue;
68
+ // Only flag if it's in a catch block or error handler that returns to client
69
+ const before = content.slice(Math.max(0, match.index - 500), match.index);
70
+ const isInCatchBlock = /catch\s*\(|except\s+/.test(before);
71
+ const returnsToClient = /return\s*\{|content.*text/.test(content.slice(match.index, Math.min(content.length, match.index + 200)));
72
+ if (isInCatchBlock && (returnsToClient || /\.stack/.test(lineContent) || /traceback/.test(lineContent))) {
73
+ findings.push({
74
+ ruleId: "MCS-CFG-002",
75
+ severity: "low",
76
+ title: "Verbose Error Messages",
77
+ message: `Error handler returns detailed error information (stack traces, internal paths). This leaks implementation details.`,
78
+ location: { file, startLine: line, endLine: line },
79
+ fix: {
80
+ description: "Return generic error messages to the client. Log detailed errors server-side only.",
81
+ suggestion: 'return { content: [{ type: "text", text: "An error occurred. Please try again." }] };',
82
+ },
83
+ });
84
+ break; // One per file is enough for this rule
85
+ }
86
+ }
87
+ }
88
+ }
89
+ return findings;
90
+ }
91
+ export function detectInsecureTransport(context) {
92
+ const findings = [];
93
+ const TRANSPORT_PATTERNS = [
94
+ { pattern: /(?:host|bind|listen)\s*[:=]\s*["']0\.0\.0\.0["']/g, label: "Server binds to all interfaces (0.0.0.0)" },
95
+ { pattern: /cors\s*[:=]\s*\{\s*origin\s*[:=]\s*["']\*["']/g, label: "CORS allows all origins" },
96
+ { pattern: /Access-Control-Allow-Origin['":\s]*\*/g, label: "CORS allows all origins" },
97
+ ];
98
+ for (const [file, content] of context.sources) {
99
+ for (const { pattern, label } of TRANSPORT_PATTERNS) {
100
+ pattern.lastIndex = 0;
101
+ let match;
102
+ while ((match = pattern.exec(content)) !== null) {
103
+ const line = findLineNumber(content, match.index);
104
+ const lineContent = content.split("\n")[line - 1] || "";
105
+ const trimmed = lineContent.trimStart();
106
+ if (trimmed.startsWith("//") || trimmed.startsWith("#") || trimmed.startsWith("*"))
107
+ continue;
108
+ findings.push({
109
+ ruleId: "MCS-CFG-003",
110
+ severity: "medium",
111
+ title: "Insecure Transport Configuration",
112
+ message: `${label}. This may expose the MCP server to unauthorized access.`,
113
+ location: { file, startLine: line, endLine: line },
114
+ fix: {
115
+ description: "Bind to localhost (127.0.0.1), use specific CORS origins, and enable TLS for HTTP transport.",
116
+ },
117
+ });
118
+ }
119
+ }
120
+ }
121
+ return findings;
122
+ }
123
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/rules/config.ts"],"names":[],"mappings":"AAEA,SAAS,cAAc,CAAC,OAAe,EAAE,KAAa;IACpD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAwB;IACtD,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,MAAM,cAAc,GAAG;QACrB,4CAA4C;QAC5C,wBAAwB;QACxB,2CAA2C;QAC3C,2BAA2B;QAC3B,iBAAiB;QACjB,iCAAiC;KAClC,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9C,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;YACrC,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;YACtB,IAAI,KAAK,CAAC;YACV,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBAChD,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBAClD,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;gBAExD,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,EAAE,CAAC;gBACxC,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAE7F,8EAA8E;gBAC9E,IAAI,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC;oBAAE,SAAS;gBAEvE,QAAQ,CAAC,IAAI,CAAC;oBACZ,MAAM,EAAE,aAAa;oBACrB,QAAQ,EAAE,QAAQ;oBAClB,KAAK,EAAE,oBAAoB;oBAC3B,OAAO,EAAE,kGAAkG;oBAC3G,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;oBAClD,GAAG,EAAE;wBACH,WAAW,EAAE,0EAA0E;qBACxF;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAwB;IAC1D,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,MAAM,mBAAmB,GAAG;QAC1B,YAAY;QACZ,mBAAmB;QACnB,yCAAyC;QACzC,wCAAwC;KACzC,CAAC;IAEF,MAAM,mBAAmB,GAAG;QAC1B,wBAAwB;QACxB,uBAAuB;QACvB,4CAA4C;KAC7C,CAAC;IAEF,MAAM,QAAQ,GACZ,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;QACrD,OAAO,CAAC,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;YACzD,CAAC,GAAG,mBAAmB,EAAE,GAAG,mBAAmB,CAAC,CAAC;IAEnD,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;YACtB,IAAI,KAAK,CAAC;YACV,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBAChD,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBAClD,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;gBAExD,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,EAAE,CAAC;gBACxC,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAE7F,6EAA6E;gBAC7E,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC1E,MAAM,cAAc,GAAG,sBAAsB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC3D,MAAM,eAAe,GAAG,2BAA2B,CAAC,IAAI,CACtD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CACxE,CAAC;gBAEF,IAAI,cAAc,IAAI,CAAC,eAAe,IAAI,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;oBACxG,QAAQ,CAAC,IAAI,CAAC;wBACZ,MAAM,EAAE,aAAa;wBACrB,QAAQ,EAAE,KAAK;wBACf,KAAK,EAAE,wBAAwB;wBAC/B,OAAO,EAAE,qHAAqH;wBAC9H,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;wBAClD,GAAG,EAAE;4BACH,WAAW,EAAE,oFAAoF;4BACjG,UAAU,EAAE,uFAAuF;yBACpG;qBACF,CAAC,CAAC;oBACH,MAAM,CAAC,uCAAuC;gBAChD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAwB;IAC9D,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,MAAM,kBAAkB,GAAG;QACzB,EAAE,OAAO,EAAE,mDAAmD,EAAE,KAAK,EAAE,0CAA0C,EAAE;QACnH,EAAE,OAAO,EAAE,gDAAgD,EAAE,KAAK,EAAE,yBAAyB,EAAE;QAC/F,EAAE,OAAO,EAAE,wCAAwC,EAAE,KAAK,EAAE,yBAAyB,EAAE;KACxF,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9C,KAAK,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,kBAAkB,EAAE,CAAC;YACpD,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;YACtB,IAAI,KAAK,CAAC;YACV,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBAChD,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBAClD,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;gBAExD,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,EAAE,CAAC;gBACxC,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAE7F,QAAQ,CAAC,IAAI,CAAC;oBACZ,MAAM,EAAE,aAAa;oBACrB,QAAQ,EAAE,QAAQ;oBAClB,KAAK,EAAE,kCAAkC;oBACzC,OAAO,EAAE,GAAG,KAAK,0DAA0D;oBAC3E,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;oBAClD,GAAG,EAAE;wBACH,WAAW,EAAE,8FAA8F;qBAC5G;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { AnalysisContext, Finding } from "../analyzers/types.js";
2
+ export declare function detectEnvVarExposure(context: AnalysisContext): Finding[];
3
+ export declare function detectCredentialLeakage(context: AnalysisContext): Finding[];
4
+ //# sourceMappingURL=data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/rules/data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAmBtE,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,EAAE,CAkCxE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,EAAE,CAmC3E"}
@@ -0,0 +1,79 @@
1
+ // Patterns that expose entire environment
2
+ const ENV_EXPOSURE_TS = [
3
+ /\bprocess\.env\b(?!\s*\.)(?!\s*\[)/g, // process.env without any key access (bare reference)
4
+ /JSON\.stringify\s*\(\s*process\.env\b/g,
5
+ /Object\.(?:keys|values|entries)\s*\(\s*process\.env\b/g,
6
+ ];
7
+ const ENV_EXPOSURE_PY = [
8
+ /\bos\.environ\b(?!\s*\.\s*get)(?!\s*\[)/g, // os.environ without .get() or []
9
+ /dict\s*\(\s*os\.environ\b/g,
10
+ /json\.dumps\s*\(\s*(?:dict\s*\(\s*)?os\.environ/g,
11
+ ];
12
+ function findLineNumber(content, index) {
13
+ return content.slice(0, index).split("\n").length;
14
+ }
15
+ export function detectEnvVarExposure(context) {
16
+ const findings = [];
17
+ const patterns = context.language === "python" ? ENV_EXPOSURE_PY :
18
+ context.language === "typescript" ? ENV_EXPOSURE_TS :
19
+ [...ENV_EXPOSURE_TS, ...ENV_EXPOSURE_PY];
20
+ for (const [file, content] of context.sources) {
21
+ for (const pattern of patterns) {
22
+ pattern.lastIndex = 0;
23
+ let match;
24
+ while ((match = pattern.exec(content)) !== null) {
25
+ const line = findLineNumber(content, match.index);
26
+ const lineContent = content.split("\n")[line - 1] || "";
27
+ const trimmed = lineContent.trimStart();
28
+ if (trimmed.startsWith("//") || trimmed.startsWith("#") || trimmed.startsWith("*"))
29
+ continue;
30
+ findings.push({
31
+ ruleId: "MCS-DATA-001",
32
+ severity: "high",
33
+ title: "Environment Variable Exposure",
34
+ message: `Entire process environment is accessed without filtering. This exposes API keys, credentials, and secrets to the LLM.`,
35
+ location: { file, startLine: line, endLine: line },
36
+ fix: {
37
+ description: "Access only specific environment variables by name, or filter out sensitive keys before returning.",
38
+ suggestion: "const safeEnv = { NODE_ENV: process.env.NODE_ENV, PORT: process.env.PORT };",
39
+ },
40
+ });
41
+ }
42
+ }
43
+ }
44
+ return findings;
45
+ }
46
+ export function detectCredentialLeakage(context) {
47
+ const findings = [];
48
+ // Patterns that return raw API responses without redaction
49
+ const LEAK_PATTERNS = [
50
+ /res\.(?:headers|data|body)\b/g,
51
+ /response\.(?:headers|data|body)\b/g,
52
+ ];
53
+ // This rule is hard to detect well with regex alone — keep it conservative
54
+ // Flag cases where raw HTTP response objects are returned in tool responses
55
+ for (const [file, content] of context.sources) {
56
+ // Look for tool handlers that return raw response data
57
+ const toolHandlerRegex = /\.tool\s*\([^)]*\)\s*[^{]*\{[\s\S]*?return\s*\{[\s\S]*?content/g;
58
+ let handlerMatch;
59
+ while ((handlerMatch = toolHandlerRegex.exec(content)) !== null) {
60
+ const handlerText = handlerMatch[0];
61
+ // Check if the handler returns raw response headers (may contain auth tokens)
62
+ if (/headers/.test(handlerText) && /JSON\.stringify/.test(handlerText)) {
63
+ const line = findLineNumber(content, handlerMatch.index);
64
+ findings.push({
65
+ ruleId: "MCS-DATA-002",
66
+ severity: "high",
67
+ title: "Credential Leakage in Tool Responses",
68
+ message: `Tool handler may return raw HTTP response headers containing authentication tokens. Filter sensitive headers before returning.`,
69
+ location: { file, startLine: line, endLine: line },
70
+ fix: {
71
+ description: "Redact authorization headers and other sensitive fields from API responses before returning to the LLM.",
72
+ },
73
+ });
74
+ }
75
+ }
76
+ }
77
+ return findings;
78
+ }
79
+ //# sourceMappingURL=data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data.js","sourceRoot":"","sources":["../../src/rules/data.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { AnalysisContext, Finding } from "../analyzers/types.js";
2
+ export declare function detectVulnerableDeps(context: AnalysisContext): Promise<Finding[]>;
3
+ //# sourceMappingURL=deps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../src/rules/deps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAY,MAAM,uBAAuB,CAAC;AA8BhF,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAwDvF"}
@@ -0,0 +1,68 @@
1
+ function mapOsvSeverity(vuln) {
2
+ if (vuln.severity && vuln.severity.length > 0) {
3
+ const score = parseFloat(vuln.severity[0].score);
4
+ if (score >= 9.0)
5
+ return "critical";
6
+ if (score >= 7.0)
7
+ return "high";
8
+ if (score >= 4.0)
9
+ return "medium";
10
+ return "low";
11
+ }
12
+ return "medium"; // default if no severity info
13
+ }
14
+ export async function detectVulnerableDeps(context) {
15
+ const findings = [];
16
+ if (!context.manifest)
17
+ return findings;
18
+ const allDeps = {
19
+ ...context.manifest.dependencies,
20
+ ...context.manifest.devDependencies,
21
+ };
22
+ const ecosystem = context.language === "python" ? "PyPI" : "npm";
23
+ for (const [name, versionSpec] of Object.entries(allDeps)) {
24
+ // Clean version spec (remove ^, ~, >=, etc.)
25
+ const version = versionSpec.replace(/^[\^~>=<]+/, "");
26
+ if (!version || version === "*" || version === "latest")
27
+ continue;
28
+ try {
29
+ const response = await fetch("https://api.osv.dev/v1/query", {
30
+ method: "POST",
31
+ headers: { "Content-Type": "application/json" },
32
+ body: JSON.stringify({
33
+ package: { name, ecosystem },
34
+ version,
35
+ }),
36
+ });
37
+ if (!response.ok)
38
+ continue;
39
+ const data = (await response.json());
40
+ if (data.vulns && data.vulns.length > 0) {
41
+ for (const vuln of data.vulns) {
42
+ findings.push({
43
+ ruleId: "MCS-DEP-001",
44
+ severity: mapOsvSeverity(vuln),
45
+ title: "Known Vulnerable Dependency",
46
+ message: `${name}@${version} has known vulnerability ${vuln.id}${vuln.summary ? `: ${vuln.summary}` : ""}`,
47
+ location: {
48
+ file: context.manifest.lockfilePath
49
+ ? context.manifest.lockfilePath.split("/").pop() || "package.json"
50
+ : "package.json",
51
+ startLine: 1,
52
+ endLine: 1,
53
+ },
54
+ fix: {
55
+ description: `Update ${name} to a patched version.`,
56
+ },
57
+ });
58
+ }
59
+ }
60
+ }
61
+ catch {
62
+ // Network error — skip this dep silently
63
+ continue;
64
+ }
65
+ }
66
+ return findings;
67
+ }
68
+ //# sourceMappingURL=deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/rules/deps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { AnalysisContext, Finding } from "../analyzers/types.js";
2
+ export declare function detectSuspiciousDescriptions(context: AnalysisContext): Finding[];
3
+ //# sourceMappingURL=description.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"description.d.ts","sourceRoot":"","sources":["../../src/rules/description.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AA8EtE,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,EAAE,CA0BhF"}
@@ -0,0 +1,91 @@
1
+ // Prompt injection patterns in tool descriptions
2
+ const INJECTION_PATTERNS = [
3
+ // Override/ignore instructions
4
+ { pattern: /ignore\s+(?:previous|prior|above|all)\s+instructions/i, label: "Instruction override pattern" },
5
+ { pattern: /disregard\s+(?:previous|prior|above|all)/i, label: "Instruction override pattern" },
6
+ { pattern: /forget\s+(?:your|all)\s+(?:previous\s+)?instructions/i, label: "Instruction override pattern" },
7
+ { pattern: /you\s+are\s+now\s+/i, label: "Role hijacking pattern" },
8
+ { pattern: /new\s+instructions?\s*:/i, label: "Instruction injection pattern" },
9
+ { pattern: /system\s*:\s*/i, label: "System prompt injection" },
10
+ { pattern: /IMPORTANT\s*:\s*(?:Before|Also|First|Always)/i, label: "Injected priority instruction" },
11
+ // Exfiltration patterns
12
+ { pattern: /~\/\.ssh/i, label: "SSH key exfiltration attempt" },
13
+ { pattern: /\.env\b/i, label: "Environment file exfiltration attempt" },
14
+ { pattern: /id_rsa/i, label: "SSH private key reference" },
15
+ { pattern: /send\s+(?:the\s+)?(?:data|content|result|response|info|information)\s+to/i, label: "Data exfiltration instruction" },
16
+ { pattern: /forward\s+(?:the\s+)?(?:data|content|result|response)\s+to/i, label: "Data forwarding instruction" },
17
+ { pattern: /https?:\/\/[^\s"']+/i, label: "URL in tool description (potential exfiltration target)" },
18
+ // Cross-tool manipulation
19
+ { pattern: /(?:call|invoke|use|execute|run)\s+the\s+\w+\s+tool/i, label: "Cross-tool invocation instruction" },
20
+ { pattern: /before\s+returning.*(?:also|first)\s+(?:read|call|send|access)/i, label: "Hidden pre-action instruction" },
21
+ { pattern: /include\s+(?:the\s+)?(?:contents?|data|output)\s+(?:of|from)/i, label: "Data inclusion instruction" },
22
+ // Hidden content patterns
23
+ { pattern: /[\u200B\u200C\u200D\u2060\uFEFF]/, label: "Zero-width Unicode characters (hidden content)" },
24
+ ];
25
+ // Find string literals that look like tool descriptions in source code
26
+ function extractDescriptionStrings(content, language) {
27
+ const descriptions = [];
28
+ const lines = content.split("\n");
29
+ if (language === "typescript" || language === "unknown") {
30
+ // Pattern: server.tool("name", "description", ...)
31
+ // The description is typically the second string argument
32
+ const toolCallRegex = /\.tool\s*\(\s*["'`][^"'`]*["'`]\s*,\s*(["'`])([\s\S]*?)\1/g;
33
+ let match;
34
+ while ((match = toolCallRegex.exec(content)) !== null) {
35
+ const desc = match[2];
36
+ const line = content.slice(0, match.index).split("\n").length;
37
+ descriptions.push({ text: desc, line });
38
+ }
39
+ // Also check multi-line template literals after tool name
40
+ const templateRegex = /\.tool\s*\(\s*["'`][^"'`]*["'`]\s*,\s*`([^`]*)`/g;
41
+ while ((match = templateRegex.exec(content)) !== null) {
42
+ const desc = match[1];
43
+ const line = content.slice(0, match.index).split("\n").length;
44
+ descriptions.push({ text: desc, line });
45
+ }
46
+ }
47
+ if (language === "python" || language === "unknown") {
48
+ // Pattern: @mcp.tool() with docstring, or description parameter
49
+ // FastMCP: @mcp.tool(description="...")
50
+ const pyDescRegex = /description\s*=\s*["']([\s\S]*?)["']/g;
51
+ let match;
52
+ while ((match = pyDescRegex.exec(content)) !== null) {
53
+ const desc = match[1];
54
+ const line = content.slice(0, match.index).split("\n").length;
55
+ descriptions.push({ text: desc, line });
56
+ }
57
+ // Also check docstrings after @mcp.tool() decorated functions
58
+ const docstringRegex = /def\s+\w+[^:]*:\s*\n\s*"""([\s\S]*?)"""/g;
59
+ while ((match = docstringRegex.exec(content)) !== null) {
60
+ const desc = match[1];
61
+ const line = content.slice(0, match.index).split("\n").length;
62
+ descriptions.push({ text: desc, line });
63
+ }
64
+ }
65
+ return descriptions;
66
+ }
67
+ export function detectSuspiciousDescriptions(context) {
68
+ const findings = [];
69
+ for (const [file, content] of context.sources) {
70
+ const descriptions = extractDescriptionStrings(content, context.language);
71
+ for (const { text, line } of descriptions) {
72
+ for (const { pattern, label } of INJECTION_PATTERNS) {
73
+ if (pattern.test(text)) {
74
+ findings.push({
75
+ ruleId: "MCS-DESC-001",
76
+ severity: "high",
77
+ title: "Suspicious Instructions in Tool Descriptions",
78
+ message: `Tool description contains ${label}. This may be tool poisoning — hidden instructions processed by the LLM but not visible to users.`,
79
+ location: { file, startLine: line, endLine: line },
80
+ fix: {
81
+ description: "Remove suspicious instructions from the tool description. Descriptions should only describe what the tool does, not instruct the model to take additional actions.",
82
+ },
83
+ });
84
+ break; // One finding per description is enough
85
+ }
86
+ }
87
+ }
88
+ }
89
+ return findings;
90
+ }
91
+ //# sourceMappingURL=description.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"description.js","sourceRoot":"","sources":["../../src/rules/description.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { RuleDefinition } from "../analyzers/types.js";
2
+ export declare const rules: RuleDefinition[];
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,uBAAuB,CAAC;AAetF,eAAO,MAAM,KAAK,EAAE,cAAc,EAoKjC,CAAC"}
@@ -0,0 +1,154 @@
1
+ import { detectCommandInjection, detectSqlInjection, detectPathTraversal } from "./injection.js";
2
+ import { detectBroadCapabilities, detectUnrestrictedFilesystem, detectArbitraryCodeExecution } from "./permissions.js";
3
+ import { detectEnvVarExposure, detectCredentialLeakage } from "./data.js";
4
+ import { detectMissingInputSchema } from "./validation.js";
5
+ import { detectSuspiciousDescriptions } from "./description.js";
6
+ import { detectHardcodedCredentials, detectSecretsInConfig } from "./auth.js";
7
+ import { detectDebugMode, detectVerboseErrors, detectInsecureTransport } from "./config.js";
8
+ // Wrapper to handle both sync and async detect functions uniformly
9
+ function syncDetect(fn) {
10
+ return fn;
11
+ }
12
+ export const rules = [
13
+ // ─── Injection ───
14
+ {
15
+ id: "MCS-INJ-001",
16
+ name: "Command Injection via Tool Input",
17
+ severity: "critical",
18
+ category: "injection",
19
+ description: "User-controlled tool inputs passed to shell execution functions (exec, execSync, spawn with shell: true)",
20
+ detect: syncDetect(detectCommandInjection),
21
+ },
22
+ {
23
+ id: "MCS-INJ-002",
24
+ name: "SQL Injection via Tool Input",
25
+ severity: "critical",
26
+ category: "injection",
27
+ description: "Tool inputs concatenated into SQL strings without parameterized queries",
28
+ detect: syncDetect(detectSqlInjection),
29
+ },
30
+ {
31
+ id: "MCS-INJ-003",
32
+ name: "Path Traversal in File Operations",
33
+ severity: "high",
34
+ category: "injection",
35
+ description: "Tool inputs used in file paths without canonicalization or directory restriction",
36
+ detect: syncDetect(detectPathTraversal),
37
+ },
38
+ // ─── Permissions ───
39
+ {
40
+ id: "MCS-PERM-001",
41
+ name: "Overly Broad Tool Capabilities",
42
+ severity: "high",
43
+ category: "permissions",
44
+ description: "Tools performing dangerous operations (file write, network, exec, DB mutations) without scope restrictions",
45
+ detect: syncDetect(detectBroadCapabilities),
46
+ },
47
+ {
48
+ id: "MCS-PERM-002",
49
+ name: "Unrestricted Filesystem Access",
50
+ severity: "high",
51
+ category: "permissions",
52
+ description: "File system tools with no directory allowlist or path prefix restriction",
53
+ detect: syncDetect(detectUnrestrictedFilesystem),
54
+ },
55
+ {
56
+ id: "MCS-PERM-003",
57
+ name: "Tool Can Execute Arbitrary Code",
58
+ severity: "critical",
59
+ category: "permissions",
60
+ description: "Tools that evaluate user input as code (eval, Function, exec in Python, vm.runInNewContext)",
61
+ detect: syncDetect(detectArbitraryCodeExecution),
62
+ },
63
+ // ─── Data Exfiltration ───
64
+ {
65
+ id: "MCS-DATA-001",
66
+ name: "Environment Variable Exposure",
67
+ severity: "high",
68
+ category: "data-exfiltration",
69
+ description: "Tools or resources that return process.env / os.environ without filtering",
70
+ detect: syncDetect(detectEnvVarExposure),
71
+ },
72
+ {
73
+ id: "MCS-DATA-002",
74
+ name: "Credential Leakage in Tool Responses",
75
+ severity: "high",
76
+ category: "data-exfiltration",
77
+ description: "Tool responses that include raw API responses containing auth tokens or credentials without redaction",
78
+ detect: syncDetect(detectCredentialLeakage),
79
+ },
80
+ // ─── Input Validation ───
81
+ {
82
+ id: "MCS-VALID-001",
83
+ name: "Missing Input Schema",
84
+ severity: "medium",
85
+ category: "validation",
86
+ description: "Tools registered without input validation schemas (empty inputSchema or no Zod/JSON Schema)",
87
+ detect: syncDetect(detectMissingInputSchema),
88
+ },
89
+ // ─── Tool Description Integrity ───
90
+ {
91
+ id: "MCS-DESC-001",
92
+ name: "Suspicious Instructions in Tool Descriptions",
93
+ severity: "high",
94
+ category: "description",
95
+ description: "Tool descriptions containing prompt injection patterns: override instructions, exfiltration URLs, cross-tool manipulation",
96
+ detect: syncDetect(detectSuspiciousDescriptions),
97
+ },
98
+ // ─── Authentication & Secrets ───
99
+ {
100
+ id: "MCS-AUTH-001",
101
+ name: "Hardcoded Credentials",
102
+ severity: "critical",
103
+ category: "auth",
104
+ description: "API keys, tokens, passwords, or connection strings hardcoded in server source",
105
+ detect: syncDetect(detectHardcodedCredentials),
106
+ },
107
+ {
108
+ id: "MCS-AUTH-002",
109
+ name: "Secrets in MCP Configuration",
110
+ severity: "high",
111
+ category: "auth",
112
+ description: "API keys or tokens directly in MCP config files (env block) rather than referenced from environment",
113
+ detect: syncDetect(detectSecretsInConfig),
114
+ },
115
+ // ─── Configuration ───
116
+ {
117
+ id: "MCS-CFG-001",
118
+ name: "Debug Mode Enabled",
119
+ severity: "medium",
120
+ category: "config",
121
+ description: "Debug or development configuration left enabled in production builds",
122
+ detect: syncDetect(detectDebugMode),
123
+ },
124
+ {
125
+ id: "MCS-CFG-002",
126
+ name: "Verbose Error Messages",
127
+ severity: "low",
128
+ category: "config",
129
+ description: "Error handlers that return full stack traces, internal paths, or system information to the client",
130
+ detect: syncDetect(detectVerboseErrors),
131
+ },
132
+ {
133
+ id: "MCS-CFG-003",
134
+ name: "Insecure Transport Configuration",
135
+ severity: "medium",
136
+ category: "config",
137
+ description: "HTTP servers without TLS, servers binding to 0.0.0.0, CORS configured with wildcard origin",
138
+ detect: syncDetect(detectInsecureTransport),
139
+ },
140
+ // ─── Dependencies ───
141
+ {
142
+ id: "MCS-DEP-001",
143
+ name: "Known Vulnerable Dependencies",
144
+ severity: "high",
145
+ category: "dependencies",
146
+ description: "Dependencies with known CVEs in package-lock.json / requirements.txt",
147
+ detect: (_ctx) => {
148
+ // DEP-001 is async (network call to OSV.dev) — skip in sync rule loop.
149
+ // It's called separately in scanner.ts.
150
+ return [];
151
+ },
152
+ },
153
+ ];
154
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { AnalysisContext, Finding } from "../analyzers/types.js";
2
+ export declare function detectCommandInjection(context: AnalysisContext): Finding[];
3
+ export declare function detectSqlInjection(context: AnalysisContext): Finding[];
4
+ export declare function detectPathTraversal(context: AnalysisContext): Finding[];
5
+ //# sourceMappingURL=injection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"injection.d.ts","sourceRoot":"","sources":["../../src/rules/injection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAkHtE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,EAAE,CA6C1E;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,EAAE,CAkCtE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,EAAE,CAwCvE"}