project-shield 1.0.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 +440 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +151 -0
- package/dist/index.js.map +1 -0
- package/dist/integrity/failsafe.d.ts +17 -0
- package/dist/integrity/failsafe.d.ts.map +1 -0
- package/dist/integrity/failsafe.js +45 -0
- package/dist/integrity/failsafe.js.map +1 -0
- package/dist/integrity/ruleset.d.ts +12 -0
- package/dist/integrity/ruleset.d.ts.map +1 -0
- package/dist/integrity/ruleset.js +77 -0
- package/dist/integrity/ruleset.js.map +1 -0
- package/dist/integrity/seal.d.ts +12 -0
- package/dist/integrity/seal.d.ts.map +1 -0
- package/dist/integrity/seal.js +77 -0
- package/dist/integrity/seal.js.map +1 -0
- package/dist/output/badge.d.ts +16 -0
- package/dist/output/badge.d.ts.map +1 -0
- package/dist/output/badge.js +112 -0
- package/dist/output/badge.js.map +1 -0
- package/dist/output/evidence.d.ts +18 -0
- package/dist/output/evidence.d.ts.map +1 -0
- package/dist/output/evidence.js +205 -0
- package/dist/output/evidence.js.map +1 -0
- package/dist/output/fixit.d.ts +32 -0
- package/dist/output/fixit.d.ts.map +1 -0
- package/dist/output/fixit.js +387 -0
- package/dist/output/fixit.js.map +1 -0
- package/dist/output/terminal.d.ts +10 -0
- package/dist/output/terminal.d.ts.map +1 -0
- package/dist/output/terminal.js +190 -0
- package/dist/output/terminal.js.map +1 -0
- package/dist/scanner/engine.d.ts +6 -0
- package/dist/scanner/engine.d.ts.map +1 -0
- package/dist/scanner/engine.js +155 -0
- package/dist/scanner/engine.js.map +1 -0
- package/dist/scanner/ignore.d.ts +20 -0
- package/dist/scanner/ignore.d.ts.map +1 -0
- package/dist/scanner/ignore.js +125 -0
- package/dist/scanner/ignore.js.map +1 -0
- package/dist/scanner/injection.d.ts +15 -0
- package/dist/scanner/injection.d.ts.map +1 -0
- package/dist/scanner/injection.js +234 -0
- package/dist/scanner/injection.js.map +1 -0
- package/dist/scanner/mcp.d.ts +6 -0
- package/dist/scanner/mcp.d.ts.map +1 -0
- package/dist/scanner/mcp.js +322 -0
- package/dist/scanner/mcp.js.map +1 -0
- package/dist/scanner/pii.d.ts +21 -0
- package/dist/scanner/pii.d.ts.map +1 -0
- package/dist/scanner/pii.js +161 -0
- package/dist/scanner/pii.js.map +1 -0
- package/dist/scanner/secrets.d.ts +10 -0
- package/dist/scanner/secrets.d.ts.map +1 -0
- package/dist/scanner/secrets.js +224 -0
- package/dist/scanner/secrets.js.map +1 -0
- package/dist/scoring/lock.d.ts +12 -0
- package/dist/scoring/lock.d.ts.map +1 -0
- package/dist/scoring/lock.js +58 -0
- package/dist/scoring/lock.js.map +1 -0
- package/dist/scoring/score.d.ts +14 -0
- package/dist/scoring/score.d.ts.map +1 -0
- package/dist/scoring/score.js +74 -0
- package/dist/scoring/score.js.map +1 -0
- package/dist/types/index.d.ts +205 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +52 -0
- package/rules/v1.0.0.json +248 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.scan = scan;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const glob_1 = require("glob");
|
|
40
|
+
const ruleset_js_1 = require("../integrity/ruleset.js");
|
|
41
|
+
const failsafe_js_1 = require("../integrity/failsafe.js");
|
|
42
|
+
const ignore_js_1 = require("./ignore.js");
|
|
43
|
+
const secrets_js_1 = require("./secrets.js");
|
|
44
|
+
const pii_js_1 = require("./pii.js");
|
|
45
|
+
const mcp_js_1 = require("./mcp.js");
|
|
46
|
+
const injection_js_1 = require("./injection.js");
|
|
47
|
+
// Binary file extensions to skip
|
|
48
|
+
const BINARY_EXTENSIONS = new Set([
|
|
49
|
+
'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.svg',
|
|
50
|
+
'.woff', '.woff2', '.ttf', '.eot', '.otf',
|
|
51
|
+
'.zip', '.tar', '.gz', '.bz2', '.7z', '.rar',
|
|
52
|
+
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
|
53
|
+
'.exe', '.dll', '.so', '.dylib', '.bin',
|
|
54
|
+
'.mp3', '.mp4', '.avi', '.mov', '.wav', '.flac',
|
|
55
|
+
'.pyc', '.class', '.o', '.obj',
|
|
56
|
+
'.lock',
|
|
57
|
+
]);
|
|
58
|
+
// Default directories to always exclude
|
|
59
|
+
const DEFAULT_EXCLUDE_DIRS = [
|
|
60
|
+
'node_modules',
|
|
61
|
+
'.git',
|
|
62
|
+
'dist',
|
|
63
|
+
'build',
|
|
64
|
+
'.next',
|
|
65
|
+
'__pycache__',
|
|
66
|
+
'.venv',
|
|
67
|
+
'venv',
|
|
68
|
+
'coverage',
|
|
69
|
+
];
|
|
70
|
+
/**
|
|
71
|
+
* Check if a file is binary by extension.
|
|
72
|
+
*/
|
|
73
|
+
function isBinaryFile(filePath) {
|
|
74
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
75
|
+
return BINARY_EXTENSIONS.has(ext);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Run the full scan pipeline.
|
|
79
|
+
*/
|
|
80
|
+
async function scan(config) {
|
|
81
|
+
return (0, failsafe_js_1.withFailsafe)(async () => {
|
|
82
|
+
const startTime = Date.now();
|
|
83
|
+
// Load ruleset
|
|
84
|
+
const ruleset = (0, ruleset_js_1.loadRuleset)(config.rulesetPath);
|
|
85
|
+
// Load ignore patterns
|
|
86
|
+
const ignorePatterns = (0, ignore_js_1.loadIgnorePatterns)(config.targetPath, config.ignorePath);
|
|
87
|
+
// Find all files
|
|
88
|
+
const allFiles = await (0, glob_1.glob)('**/*', {
|
|
89
|
+
cwd: config.targetPath,
|
|
90
|
+
nodir: true,
|
|
91
|
+
dot: true,
|
|
92
|
+
ignore: DEFAULT_EXCLUDE_DIRS.map(d => `${d}/**`),
|
|
93
|
+
absolute: false,
|
|
94
|
+
});
|
|
95
|
+
let filesExcluded = 0;
|
|
96
|
+
const filesToScan = [];
|
|
97
|
+
for (const file of allFiles) {
|
|
98
|
+
// Skip binary files
|
|
99
|
+
if (isBinaryFile(file)) {
|
|
100
|
+
filesExcluded++;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
// Skip ignored files
|
|
104
|
+
if ((0, ignore_js_1.isFileIgnored)(file, ignorePatterns)) {
|
|
105
|
+
filesExcluded++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
filesToScan.push(file);
|
|
109
|
+
}
|
|
110
|
+
// Scan each file
|
|
111
|
+
const allSecrets = [];
|
|
112
|
+
const allPII = [];
|
|
113
|
+
const allInjection = [];
|
|
114
|
+
for (const file of filesToScan) {
|
|
115
|
+
const fullFilePath = path.join(config.targetPath, file);
|
|
116
|
+
let content;
|
|
117
|
+
try {
|
|
118
|
+
content = fs.readFileSync(fullFilePath, 'utf-8');
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Skip files that can't be read
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const secrets = (0, secrets_js_1.scanFileSecrets)(content, file, ruleset);
|
|
125
|
+
const pii = (0, pii_js_1.scanFilePII)(content, file, ruleset);
|
|
126
|
+
const injection = (0, injection_js_1.scanFileInjection)(content, file, ruleset);
|
|
127
|
+
allSecrets.push(...secrets);
|
|
128
|
+
allPII.push(...pii);
|
|
129
|
+
allInjection.push(...injection);
|
|
130
|
+
}
|
|
131
|
+
// MCP scan (after secrets, to cross-reference)
|
|
132
|
+
const allMCP = await (0, mcp_js_1.scanMCPConfigs)(config.targetPath, ruleset, allSecrets);
|
|
133
|
+
const timeMs = Date.now() - startTime;
|
|
134
|
+
return {
|
|
135
|
+
secrets: allSecrets,
|
|
136
|
+
pii: allPII,
|
|
137
|
+
mcp: allMCP,
|
|
138
|
+
injection: allInjection,
|
|
139
|
+
summary: {
|
|
140
|
+
filesScanned: filesToScan.length,
|
|
141
|
+
filesExcluded,
|
|
142
|
+
timeMs,
|
|
143
|
+
critical: allSecrets.filter(s => s.severity === 'critical').length,
|
|
144
|
+
warning: allSecrets.filter(s => s.severity === 'warning').length,
|
|
145
|
+
confirmedPii: allPII.filter(p => p.severity === 'confirmed').length,
|
|
146
|
+
possiblePii: allPII.filter(p => p.severity === 'possible').length,
|
|
147
|
+
mcpCritical: allMCP.filter(m => m.overallSeverity === 'critical').length,
|
|
148
|
+
mcpWarning: allMCP.filter(m => m.overallSeverity === 'warning').length,
|
|
149
|
+
injectionCritical: allInjection.filter(j => j.severity === 'critical').length,
|
|
150
|
+
injectionWarning: allInjection.filter(j => j.severity === 'warning').length,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/scanner/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a line has a shield-ignore comment.
|
|
3
|
+
* Supports: // shield-ignore, # shield-ignore, /* shield-ignore */
|
|
4
|
+
*/
|
|
5
|
+
export declare function isLineIgnored(line: string): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Parse a .shieldignore file (same format as .gitignore).
|
|
8
|
+
* Returns an array of glob patterns to exclude.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseShieldIgnore(ignorePath: string): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Check if a file path matches any of the ignore patterns.
|
|
13
|
+
* Uses simple glob matching (supports * and ** patterns).
|
|
14
|
+
*/
|
|
15
|
+
export declare function isFileIgnored(filePath: string, patterns: string[]): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Load ignore patterns from a .shieldignore file, resolving relative to a base directory.
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadIgnorePatterns(basePath: string, customIgnorePath?: string): string[];
|
|
20
|
+
//# sourceMappingURL=ignore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore.d.ts","sourceRoot":"","sources":["../../src/scanner/ignore.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQnD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAU9D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAmB3E;AA0BD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAaxF"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.isLineIgnored = isLineIgnored;
|
|
37
|
+
exports.parseShieldIgnore = parseShieldIgnore;
|
|
38
|
+
exports.isFileIgnored = isFileIgnored;
|
|
39
|
+
exports.loadIgnorePatterns = loadIgnorePatterns;
|
|
40
|
+
const fs = __importStar(require("node:fs"));
|
|
41
|
+
const path = __importStar(require("node:path"));
|
|
42
|
+
/**
|
|
43
|
+
* Check if a line has a shield-ignore comment.
|
|
44
|
+
* Supports: // shield-ignore, # shield-ignore, /* shield-ignore */
|
|
45
|
+
*/
|
|
46
|
+
function isLineIgnored(line) {
|
|
47
|
+
const trimmed = line.trim();
|
|
48
|
+
// Check for inline comments
|
|
49
|
+
return (trimmed.includes('// shield-ignore') ||
|
|
50
|
+
trimmed.includes('# shield-ignore') ||
|
|
51
|
+
trimmed.includes('/* shield-ignore'));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse a .shieldignore file (same format as .gitignore).
|
|
55
|
+
* Returns an array of glob patterns to exclude.
|
|
56
|
+
*/
|
|
57
|
+
function parseShieldIgnore(ignorePath) {
|
|
58
|
+
if (!fs.existsSync(ignorePath)) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const content = fs.readFileSync(ignorePath, 'utf-8');
|
|
62
|
+
return content
|
|
63
|
+
.split('\n')
|
|
64
|
+
.map(line => line.trim())
|
|
65
|
+
.filter(line => line.length > 0 && !line.startsWith('#'));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if a file path matches any of the ignore patterns.
|
|
69
|
+
* Uses simple glob matching (supports * and ** patterns).
|
|
70
|
+
*/
|
|
71
|
+
function isFileIgnored(filePath, patterns) {
|
|
72
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
73
|
+
for (const pattern of patterns) {
|
|
74
|
+
const normalizedPattern = pattern.replace(/\\/g, '/');
|
|
75
|
+
// Direct match
|
|
76
|
+
if (normalized === normalizedPattern)
|
|
77
|
+
return true;
|
|
78
|
+
// Check if the file is inside an ignored directory
|
|
79
|
+
if (normalizedPattern.endsWith('/')) {
|
|
80
|
+
if (normalized.startsWith(normalizedPattern))
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
// Simple wildcard matching
|
|
84
|
+
if (matchGlob(normalized, normalizedPattern))
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Simple glob matcher supporting * and ** patterns.
|
|
91
|
+
* ** matches zero or more directories.
|
|
92
|
+
*/
|
|
93
|
+
function matchGlob(filePath, pattern) {
|
|
94
|
+
// Handle **/ specifically: it should match zero or more directories
|
|
95
|
+
// Replace **/ with a special token first
|
|
96
|
+
let regexStr = pattern
|
|
97
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
98
|
+
.replace(/\*\*\//g, '{{GLOBSTAR_SLASH}}')
|
|
99
|
+
.replace(/\*\*/g, '{{GLOBSTAR}}')
|
|
100
|
+
.replace(/\*/g, '[^/]*')
|
|
101
|
+
.replace(/\{\{GLOBSTAR_SLASH\}\}/g, '([^/]+/)*') // zero or more directories
|
|
102
|
+
.replace(/\{\{GLOBSTAR\}\}/g, '.*')
|
|
103
|
+
.replace(/\?/g, '[^/]');
|
|
104
|
+
const regex = new RegExp(`^${regexStr}$`);
|
|
105
|
+
if (regex.test(filePath))
|
|
106
|
+
return true;
|
|
107
|
+
// Also check if the pattern matches as a directory prefix
|
|
108
|
+
const dirRegex = new RegExp(`(^|/)${regexStr}(/|$)`);
|
|
109
|
+
return dirRegex.test(filePath);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Load ignore patterns from a .shieldignore file, resolving relative to a base directory.
|
|
113
|
+
*/
|
|
114
|
+
function loadIgnorePatterns(basePath, customIgnorePath) {
|
|
115
|
+
const patterns = [];
|
|
116
|
+
// Load from default .shieldignore in base path
|
|
117
|
+
const defaultIgnore = path.join(basePath, '.shieldignore');
|
|
118
|
+
patterns.push(...parseShieldIgnore(defaultIgnore));
|
|
119
|
+
// Load from custom path if provided
|
|
120
|
+
if (customIgnorePath && customIgnorePath !== defaultIgnore) {
|
|
121
|
+
patterns.push(...parseShieldIgnore(customIgnorePath));
|
|
122
|
+
}
|
|
123
|
+
return patterns;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=ignore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore.js","sourceRoot":"","sources":["../../src/scanner/ignore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { InjectionFinding, Ruleset } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Scan a single file for prompt injection patterns using 2-layer detection.
|
|
4
|
+
*
|
|
5
|
+
* Layer 1: Keyword/pattern matching (direct + indirect patterns)
|
|
6
|
+
* Layer 2: Structural analysis (comments, zero-width chars, tool length)
|
|
7
|
+
*
|
|
8
|
+
* Cross-judgment:
|
|
9
|
+
* - Layer 1 + Layer 2 = critical
|
|
10
|
+
* - Layer 1 only = warning
|
|
11
|
+
* - Layer 2 only (zero-width/comment with injection) = warning
|
|
12
|
+
* - Encoded bypass (Base64/URL) = automatic critical
|
|
13
|
+
*/
|
|
14
|
+
export declare function scanFileInjection(content: string, filePath: string, ruleset: Ruleset): InjectionFinding[];
|
|
15
|
+
//# sourceMappingURL=injection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injection.d.ts","sourceRoot":"","sources":["../../src/scanner/injection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAoB,OAAO,EAAE,MAAM,mBAAmB,CAAC;AA8IrF;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,GACf,gBAAgB,EAAE,CAiHpB"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scanFileInjection = scanFileInjection;
|
|
4
|
+
const ignore_js_1 = require("./ignore.js");
|
|
5
|
+
/**
|
|
6
|
+
* Extract surrounding context (up to 50 chars) around a match position.
|
|
7
|
+
*/
|
|
8
|
+
function extractContext(line, matchIdx, matchLen) {
|
|
9
|
+
const contextRadius = 25;
|
|
10
|
+
const start = Math.max(0, matchIdx - contextRadius);
|
|
11
|
+
const end = Math.min(line.length, matchIdx + matchLen + contextRadius);
|
|
12
|
+
let ctx = line.substring(start, end).trim();
|
|
13
|
+
if (ctx.length > 50) {
|
|
14
|
+
ctx = ctx.substring(0, 50);
|
|
15
|
+
}
|
|
16
|
+
return ctx;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Layer 1: Keyword/pattern matching against a line.
|
|
20
|
+
*/
|
|
21
|
+
function keywordLayer(line, patterns) {
|
|
22
|
+
const hits = [];
|
|
23
|
+
for (const pattern of patterns) {
|
|
24
|
+
const regex = new RegExp(pattern.regex, 'gi');
|
|
25
|
+
let match;
|
|
26
|
+
while ((match = regex.exec(line)) !== null) {
|
|
27
|
+
hits.push({ pattern, match });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return hits;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if text contains a Base64 encoded string, decode it, and re-check patterns.
|
|
34
|
+
*/
|
|
35
|
+
function decodeAndCheck(text, allPatterns) {
|
|
36
|
+
// Match Base64 strings (at least 20 chars to reduce FP)
|
|
37
|
+
const base64Regex = /[A-Za-z0-9+/]{20,}={0,2}/g;
|
|
38
|
+
let b64Match;
|
|
39
|
+
while ((b64Match = base64Regex.exec(text)) !== null) {
|
|
40
|
+
try {
|
|
41
|
+
const decoded = Buffer.from(b64Match[0], 'base64').toString('utf-8');
|
|
42
|
+
// Check if decoded text is printable (reduce FP from random base64)
|
|
43
|
+
if (!/^[\x20-\x7E\s]{4,}$/.test(decoded))
|
|
44
|
+
continue;
|
|
45
|
+
const hits = keywordLayer(decoded, allPatterns);
|
|
46
|
+
if (hits.length > 0) {
|
|
47
|
+
return { decoded, hits };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Not valid base64, skip
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// URL encoding check
|
|
55
|
+
if (text.includes('%')) {
|
|
56
|
+
try {
|
|
57
|
+
const decoded = decodeURIComponent(text);
|
|
58
|
+
if (decoded !== text) {
|
|
59
|
+
const hits = keywordLayer(decoded, allPatterns);
|
|
60
|
+
if (hits.length > 0) {
|
|
61
|
+
return { decoded, hits };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Not valid URL encoding, skip
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Layer 2: Structural analysis on content/line.
|
|
73
|
+
*/
|
|
74
|
+
function structureLayer(fullContent, line, lineIdx, ruleset) {
|
|
75
|
+
const structural = ruleset.injection.structural;
|
|
76
|
+
// Check if line is inside an HTML comment
|
|
77
|
+
let inComment = false;
|
|
78
|
+
let commentText;
|
|
79
|
+
const htmlCommentRegex = new RegExp(structural.html_comment_regex, 'g');
|
|
80
|
+
let htmlMatch;
|
|
81
|
+
while ((htmlMatch = htmlCommentRegex.exec(fullContent)) !== null) {
|
|
82
|
+
const commentStart = fullContent.substring(0, htmlMatch.index).split('\n').length - 1;
|
|
83
|
+
const commentEnd = fullContent.substring(0, htmlMatch.index + htmlMatch[0].length).split('\n').length - 1;
|
|
84
|
+
if (lineIdx >= commentStart && lineIdx <= commentEnd) {
|
|
85
|
+
inComment = true;
|
|
86
|
+
commentText = htmlMatch[0].replace(/<!--\s*/, '').replace(/\s*-->/, '');
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Check for markdown comments
|
|
91
|
+
if (!inComment) {
|
|
92
|
+
const mdCommentRegex = new RegExp(structural.markdown_comment_regex, 'g');
|
|
93
|
+
let mdMatch;
|
|
94
|
+
while ((mdMatch = mdCommentRegex.exec(line)) !== null) {
|
|
95
|
+
inComment = true;
|
|
96
|
+
commentText = mdMatch[0];
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Check for zero-width characters
|
|
101
|
+
const zeroWidthChars = ['\u200B', '\u200C', '\u200D', '\uFEFF', '\u2060'];
|
|
102
|
+
const hasZeroWidth = zeroWidthChars.some(ch => line.includes(ch));
|
|
103
|
+
// Tool description length anomaly (for JSON files with tool descriptions)
|
|
104
|
+
let toolLengthAnomaly = false;
|
|
105
|
+
try {
|
|
106
|
+
if (line.includes('"description"')) {
|
|
107
|
+
const descMatch = /"description"\s*:\s*"([^"]*)"/.exec(line);
|
|
108
|
+
if (descMatch && descMatch[1].length > 200 * structural.tool_length_multiplier) {
|
|
109
|
+
toolLengthAnomaly = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Ignore parsing errors
|
|
115
|
+
}
|
|
116
|
+
return { inComment, hasZeroWidth, toolLengthAnomaly, commentText };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Scan a single file for prompt injection patterns using 2-layer detection.
|
|
120
|
+
*
|
|
121
|
+
* Layer 1: Keyword/pattern matching (direct + indirect patterns)
|
|
122
|
+
* Layer 2: Structural analysis (comments, zero-width chars, tool length)
|
|
123
|
+
*
|
|
124
|
+
* Cross-judgment:
|
|
125
|
+
* - Layer 1 + Layer 2 = critical
|
|
126
|
+
* - Layer 1 only = warning
|
|
127
|
+
* - Layer 2 only (zero-width/comment with injection) = warning
|
|
128
|
+
* - Encoded bypass (Base64/URL) = automatic critical
|
|
129
|
+
*/
|
|
130
|
+
function scanFileInjection(content, filePath, ruleset) {
|
|
131
|
+
const findings = [];
|
|
132
|
+
const lines = content.split('\n');
|
|
133
|
+
const allPatterns = [
|
|
134
|
+
...ruleset.injection.direct_patterns,
|
|
135
|
+
...ruleset.injection.indirect_patterns,
|
|
136
|
+
];
|
|
137
|
+
for (let i = 0; i < lines.length; i++) {
|
|
138
|
+
const line = lines[i];
|
|
139
|
+
const lineNum = i + 1;
|
|
140
|
+
if ((0, ignore_js_1.isLineIgnored)(line))
|
|
141
|
+
continue;
|
|
142
|
+
// Layer 1: Keyword matching
|
|
143
|
+
const directHits = keywordLayer(line, ruleset.injection.direct_patterns);
|
|
144
|
+
const indirectHits = keywordLayer(line, ruleset.injection.indirect_patterns);
|
|
145
|
+
const allHits = [...directHits, ...indirectHits];
|
|
146
|
+
const hasKeyword = allHits.length > 0;
|
|
147
|
+
// Layer 2: Structural analysis
|
|
148
|
+
const structure = structureLayer(content, line, i, ruleset);
|
|
149
|
+
const hasStructure = structure.inComment || structure.hasZeroWidth || structure.toolLengthAnomaly;
|
|
150
|
+
// Check for encoded injection
|
|
151
|
+
const encodedResult = decodeAndCheck(line, allPatterns);
|
|
152
|
+
if (encodedResult) {
|
|
153
|
+
const firstHit = encodedResult.hits[0];
|
|
154
|
+
findings.push({
|
|
155
|
+
file: filePath,
|
|
156
|
+
line: lineNum,
|
|
157
|
+
type: 'encoded',
|
|
158
|
+
severity: 'critical',
|
|
159
|
+
layers: { keyword: true, structure: false },
|
|
160
|
+
pattern: firstHit.pattern.id,
|
|
161
|
+
context: extractContext(line, 0, Math.min(line.length, 50)),
|
|
162
|
+
description: `Encoded injection detected (${firstHit.pattern.description})`,
|
|
163
|
+
});
|
|
164
|
+
continue; // Don't double-report
|
|
165
|
+
}
|
|
166
|
+
// Cross-judgment for keyword hits
|
|
167
|
+
if (hasKeyword) {
|
|
168
|
+
for (const hit of allHits) {
|
|
169
|
+
const matchIdx = hit.match.index ?? 0;
|
|
170
|
+
const severity = hasStructure ? 'critical' : 'warning';
|
|
171
|
+
const type = hit.pattern.type === 'direct' ? 'direct' : 'indirect';
|
|
172
|
+
findings.push({
|
|
173
|
+
file: filePath,
|
|
174
|
+
line: lineNum,
|
|
175
|
+
type,
|
|
176
|
+
severity,
|
|
177
|
+
layers: { keyword: true, structure: hasStructure },
|
|
178
|
+
pattern: hit.pattern.id,
|
|
179
|
+
context: extractContext(line, matchIdx, hit.match[0].length),
|
|
180
|
+
description: hit.pattern.description,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Structure-only findings
|
|
185
|
+
if (!hasKeyword && hasStructure) {
|
|
186
|
+
// For comments, re-check the comment text for injection patterns
|
|
187
|
+
if (structure.inComment && structure.commentText) {
|
|
188
|
+
const commentHits = keywordLayer(structure.commentText, allPatterns);
|
|
189
|
+
if (commentHits.length > 0) {
|
|
190
|
+
for (const hit of commentHits) {
|
|
191
|
+
findings.push({
|
|
192
|
+
file: filePath,
|
|
193
|
+
line: lineNum,
|
|
194
|
+
type: 'structural',
|
|
195
|
+
severity: 'critical',
|
|
196
|
+
layers: { keyword: true, structure: true },
|
|
197
|
+
pattern: hit.pattern.id,
|
|
198
|
+
context: extractContext(line, 0, Math.min(line.length, 50)),
|
|
199
|
+
description: `Hidden injection in comment (${hit.pattern.description})`,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Zero-width character detection
|
|
205
|
+
if (structure.hasZeroWidth) {
|
|
206
|
+
findings.push({
|
|
207
|
+
file: filePath,
|
|
208
|
+
line: lineNum,
|
|
209
|
+
type: 'structural',
|
|
210
|
+
severity: 'warning',
|
|
211
|
+
layers: { keyword: false, structure: true },
|
|
212
|
+
pattern: 'zero_width_chars',
|
|
213
|
+
context: extractContext(line, 0, Math.min(line.length, 50)),
|
|
214
|
+
description: 'Zero-width Unicode characters detected (potential text hiding)',
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
// Tool length anomaly
|
|
218
|
+
if (structure.toolLengthAnomaly) {
|
|
219
|
+
findings.push({
|
|
220
|
+
file: filePath,
|
|
221
|
+
line: lineNum,
|
|
222
|
+
type: 'structural',
|
|
223
|
+
severity: 'warning',
|
|
224
|
+
layers: { keyword: false, structure: true },
|
|
225
|
+
pattern: 'tool_length_anomaly',
|
|
226
|
+
context: extractContext(line, 0, Math.min(line.length, 50)),
|
|
227
|
+
description: 'Abnormally long tool description (potential injection payload)',
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return findings;
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=injection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injection.js","sourceRoot":"","sources":["../../src/scanner/injection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MCPFinding, SecretFinding, Ruleset } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Scan for MCP configuration files and check 5 security items.
|
|
4
|
+
*/
|
|
5
|
+
export declare function scanMCPConfigs(targetPath: string, ruleset: Ruleset, secretsFindings: SecretFinding[]): Promise<MCPFinding[]>;
|
|
6
|
+
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/scanner/mcp.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EAEV,aAAa,EACb,OAAO,EACR,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,eAAe,EAAE,aAAa,EAAE,GAC/B,OAAO,CAAC,UAAU,EAAE,CAAC,CAiCvB"}
|