@securecode-ai/mcp 0.2.1 → 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.
- package/dist/dependency/depCache.d.ts +22 -0
- package/dist/dependency/depCache.js +115 -0
- package/dist/dependency/depCache.js.map +1 -0
- package/dist/dependency/dependencyChecker.d.ts +48 -0
- package/dist/dependency/dependencyChecker.js +196 -0
- package/dist/dependency/dependencyChecker.js.map +1 -0
- package/dist/dependency/finding.d.ts +42 -0
- package/dist/dependency/finding.js +13 -0
- package/dist/dependency/finding.js.map +1 -0
- package/dist/dependency/licensePolicy.d.ts +56 -0
- package/dist/dependency/licensePolicy.js +109 -0
- package/dist/dependency/licensePolicy.js.map +1 -0
- package/dist/dependency/minSafeVersion.d.ts +32 -0
- package/dist/dependency/minSafeVersion.js +125 -0
- package/dist/dependency/minSafeVersion.js.map +1 -0
- package/dist/dependency/osvClient.d.ts +45 -0
- package/dist/dependency/osvClient.js +452 -0
- package/dist/dependency/osvClient.js.map +1 -0
- package/dist/dependency/parsers/npm.d.ts +13 -0
- package/dist/dependency/parsers/npm.js +160 -0
- package/dist/dependency/parsers/npm.js.map +1 -0
- package/dist/dependency/parsers/pipfile.d.ts +8 -0
- package/dist/dependency/parsers/pipfile.js +110 -0
- package/dist/dependency/parsers/pipfile.js.map +1 -0
- package/dist/dependency/parsers/pnpm.d.ts +11 -0
- package/dist/dependency/parsers/pnpm.js +148 -0
- package/dist/dependency/parsers/pnpm.js.map +1 -0
- package/dist/dependency/parsers/requirements.d.ts +24 -0
- package/dist/dependency/parsers/requirements.js +153 -0
- package/dist/dependency/parsers/requirements.js.map +1 -0
- package/dist/dependency/parsers/yarn.d.ts +10 -0
- package/dist/dependency/parsers/yarn.js +153 -0
- package/dist/dependency/parsers/yarn.js.map +1 -0
- package/dist/dependency/resolver.d.ts +28 -0
- package/dist/dependency/resolver.js +159 -0
- package/dist/dependency/resolver.js.map +1 -0
- package/dist/dependency/types.d.ts +85 -0
- package/dist/dependency/types.js +11 -0
- package/dist/dependency/types.js.map +1 -0
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.js +8 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/project-map/sinkFinder.js +41 -0
- package/dist/project-map/sinkFinder.js.map +1 -1
- package/dist/project-map/sinkRegistry.d.ts +8 -0
- package/dist/project-map/sinkRegistry.js +145 -0
- package/dist/project-map/sinkRegistry.js.map +1 -1
- package/dist/tools/scanDependencies.d.ts +11 -0
- package/dist/tools/scanDependencies.js +37 -0
- package/dist/tools/scanDependencies.js.map +1 -0
- package/package.json +9 -2
|
@@ -0,0 +1,153 @@
|
|
|
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.parseRequirementsTxt = parseRequirementsTxt;
|
|
37
|
+
exports.normalizePipName = normalizePipName;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
/**
|
|
40
|
+
* Parse a `requirements.txt` file.
|
|
41
|
+
*
|
|
42
|
+
* Supported pins:
|
|
43
|
+
* name==1.2.3
|
|
44
|
+
* name>=1.2.3
|
|
45
|
+
* name~=1.2.3
|
|
46
|
+
* name>1.0,<2.0
|
|
47
|
+
* name (unpinned — marked unresolved)
|
|
48
|
+
*
|
|
49
|
+
* `#`-comments, blank lines, `-r other.txt`, `-e .`, and VCS/URL specs are
|
|
50
|
+
* skipped (URL specs are treated as unpinned/unresolved under the package
|
|
51
|
+
* name extracted from the URL when possible, otherwise skipped entirely).
|
|
52
|
+
*
|
|
53
|
+
* IMPORTANT: no network calls are made. When a pin is not an exact `==`,
|
|
54
|
+
* the entry is marked `unresolved` and surfaced to the user as such rather
|
|
55
|
+
* than queried against OSV (since the installed version is unknown). A
|
|
56
|
+
* Pipfile.lock alongside the requirements.txt will be preferred when
|
|
57
|
+
* present (handled by the resolver/dependencyChecker, not here).
|
|
58
|
+
*/
|
|
59
|
+
function parseRequirementsTxt(lockfilePath) {
|
|
60
|
+
let raw;
|
|
61
|
+
try {
|
|
62
|
+
raw = fs.readFileSync(lockfilePath, 'utf8');
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const manifestPath = lockfilePath; // requirements.txt IS the manifest
|
|
68
|
+
const packages = [];
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
71
|
+
let s = line.trim();
|
|
72
|
+
if (!s)
|
|
73
|
+
continue;
|
|
74
|
+
// Strip inline comment.
|
|
75
|
+
const hash = s.indexOf('#');
|
|
76
|
+
if (hash >= 0)
|
|
77
|
+
s = s.slice(0, hash).trim();
|
|
78
|
+
if (!s)
|
|
79
|
+
continue;
|
|
80
|
+
// Skip option lines and recursive includes; we don't follow them.
|
|
81
|
+
if (s.startsWith('-'))
|
|
82
|
+
continue;
|
|
83
|
+
if (s.startsWith('-e ') || s.startsWith('--'))
|
|
84
|
+
continue;
|
|
85
|
+
// Skip VCS / URL specs entirely (we cannot resolve them offline).
|
|
86
|
+
// Examples: git+https://..., https://..., file:///...
|
|
87
|
+
if (/^[a-z]+(\+[a-z]+)?:\/\//i.test(s))
|
|
88
|
+
continue;
|
|
89
|
+
// Strip environment markers: "name==1.0 ; python_version>='3.8'"
|
|
90
|
+
const semi = s.indexOf(';');
|
|
91
|
+
if (semi >= 0)
|
|
92
|
+
s = s.slice(0, semi).trim();
|
|
93
|
+
if (!s)
|
|
94
|
+
continue;
|
|
95
|
+
// Strip extras: "name[extra1,extra2]==1.0"
|
|
96
|
+
const nameMatch = s.match(/^([A-Za-z0-9][A-Za-z0-9._-]*)\s*(\[[^\]]*\])?\s*(.*)$/);
|
|
97
|
+
if (!nameMatch)
|
|
98
|
+
continue;
|
|
99
|
+
const rawName = nameMatch[1];
|
|
100
|
+
const spec = (nameMatch[3] || '').trim();
|
|
101
|
+
const name = normalizePipName(rawName);
|
|
102
|
+
if (seen.has(name))
|
|
103
|
+
continue;
|
|
104
|
+
seen.add(name);
|
|
105
|
+
// Exact pin: "name==1.2.3"
|
|
106
|
+
const exact = spec.match(/^==\s*([A-Za-z0-9_.!+*-]+)$/);
|
|
107
|
+
if (exact) {
|
|
108
|
+
packages.push({
|
|
109
|
+
ecosystem: 'pip',
|
|
110
|
+
name,
|
|
111
|
+
version: exact[1],
|
|
112
|
+
manifestPath,
|
|
113
|
+
direct: true,
|
|
114
|
+
});
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
// Range pins (>=, >, ~=, <=, <, comma-separated): unresolved.
|
|
118
|
+
if (spec && /([<>=~!]=?)/.test(spec)) {
|
|
119
|
+
packages.push({
|
|
120
|
+
ecosystem: 'pip',
|
|
121
|
+
name,
|
|
122
|
+
version: spec, // best-effort: keep the spec for display
|
|
123
|
+
manifestPath,
|
|
124
|
+
direct: true,
|
|
125
|
+
unresolved: true,
|
|
126
|
+
});
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
// Bare "name" (no pin): unresolved.
|
|
130
|
+
if (!spec) {
|
|
131
|
+
packages.push({
|
|
132
|
+
ecosystem: 'pip',
|
|
133
|
+
name,
|
|
134
|
+
version: '',
|
|
135
|
+
manifestPath,
|
|
136
|
+
direct: true,
|
|
137
|
+
unresolved: true,
|
|
138
|
+
});
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
ecosystem: 'pip',
|
|
144
|
+
lockfilePath,
|
|
145
|
+
manifestPath,
|
|
146
|
+
packages,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/** Normalize a PyPI package name per PEP 503 (lowercase, runs of [-_.] -> "-"). */
|
|
150
|
+
function normalizePipName(name) {
|
|
151
|
+
return name.toLowerCase().replace(/[-_.]+/g, '-');
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=requirements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requirements.js","sourceRoot":"","sources":["../../../src/dependency/parsers/requirements.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ParsedLockfile } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a yarn v1 `yarn.lock` (the classic format). v2+ (Berry) uses a
|
|
4
|
+
* different text format and is NOT covered here — we deliberately bail in
|
|
5
|
+
* that case so the caller can skip the file gracefully.
|
|
6
|
+
*
|
|
7
|
+
* yarn v1 lockfiles contain the resolved transitive tree: every package
|
|
8
|
+
* entry has a `version` and a `resolved` URL, including transitive deps.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseYarnLockfile(lockfilePath: string): ParsedLockfile | null;
|
|
@@ -0,0 +1,153 @@
|
|
|
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.parseYarnLockfile = parseYarnLockfile;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const lockfile_1 = require("@yarnpkg/lockfile");
|
|
40
|
+
/**
|
|
41
|
+
* Parse a yarn v1 `yarn.lock` (the classic format). v2+ (Berry) uses a
|
|
42
|
+
* different text format and is NOT covered here — we deliberately bail in
|
|
43
|
+
* that case so the caller can skip the file gracefully.
|
|
44
|
+
*
|
|
45
|
+
* yarn v1 lockfiles contain the resolved transitive tree: every package
|
|
46
|
+
* entry has a `version` and a `resolved` URL, including transitive deps.
|
|
47
|
+
*/
|
|
48
|
+
function parseYarnLockfile(lockfilePath) {
|
|
49
|
+
let raw;
|
|
50
|
+
try {
|
|
51
|
+
raw = fs.readFileSync(lockfilePath, 'utf8');
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
// Detect Yarn Berry (v2+) which uses a different format. The classic
|
|
57
|
+
// parser will produce garbage on it, so bail.
|
|
58
|
+
if (raw.includes('__metadata:')) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
let parsed;
|
|
62
|
+
try {
|
|
63
|
+
parsed = (0, lockfile_1.parse)(raw);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
if (!parsed || parsed.type !== 'success') {
|
|
69
|
+
// 'merge' / 'conflict' lockfiles are not directly usable.
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const manifestPath = path.join(path.dirname(lockfilePath), 'package.json');
|
|
73
|
+
let projectLicense;
|
|
74
|
+
try {
|
|
75
|
+
const pkg = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
76
|
+
if (typeof pkg.license === 'string')
|
|
77
|
+
projectLicense = pkg.license;
|
|
78
|
+
else if (pkg.license && typeof pkg.license === 'object' && typeof pkg.license.type === 'string') {
|
|
79
|
+
projectLicense = pkg.license.type;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// optional
|
|
84
|
+
}
|
|
85
|
+
// Collect direct dependency names from package.json so we can flag direct vs transitive.
|
|
86
|
+
const directNames = new Set();
|
|
87
|
+
try {
|
|
88
|
+
const pkg = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
89
|
+
for (const k of Object.keys(pkg.dependencies || {}))
|
|
90
|
+
directNames.add(k);
|
|
91
|
+
for (const k of Object.keys(pkg.devDependencies || {}))
|
|
92
|
+
directNames.add(k);
|
|
93
|
+
for (const k of Object.keys(pkg.optionalDependencies || {}))
|
|
94
|
+
directNames.add(k);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// optional
|
|
98
|
+
}
|
|
99
|
+
const packages = [];
|
|
100
|
+
const seen = new Set();
|
|
101
|
+
const obj = parsed.object;
|
|
102
|
+
for (const [key, entry] of Object.entries(obj)) {
|
|
103
|
+
// Key looks like "@scope/name@version, @scope/name@npm:^1.2.3, ..."
|
|
104
|
+
// The actual package name is everything up to the last "@" that
|
|
105
|
+
// is not part of a scoped leading "@". We pull the canonical name
|
|
106
|
+
// from the entry itself when possible (yarn exposes `name` only in
|
|
107
|
+
// some versions), otherwise from the key.
|
|
108
|
+
const name = extractYarnName(key);
|
|
109
|
+
const version = entry.version;
|
|
110
|
+
if (!name || !version)
|
|
111
|
+
continue;
|
|
112
|
+
const id = `${name}@${version}`;
|
|
113
|
+
if (seen.has(id))
|
|
114
|
+
continue;
|
|
115
|
+
seen.add(id);
|
|
116
|
+
packages.push({
|
|
117
|
+
ecosystem: 'yarn',
|
|
118
|
+
name,
|
|
119
|
+
version,
|
|
120
|
+
manifestPath,
|
|
121
|
+
direct: directNames.has(name),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
ecosystem: 'yarn',
|
|
126
|
+
lockfilePath,
|
|
127
|
+
manifestPath,
|
|
128
|
+
packages,
|
|
129
|
+
projectLicense,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Extract the package name from a yarn lockfile key.
|
|
134
|
+
* Examples:
|
|
135
|
+
* "lodash@^4.17.19" -> "lodash"
|
|
136
|
+
* "@babel/core@^7.0.0" -> "@babel/core"
|
|
137
|
+
* "lodash@npm:^4.17.19, lodash@^4.17.20" -> "lodash"
|
|
138
|
+
*/
|
|
139
|
+
function extractYarnName(key) {
|
|
140
|
+
const first = key.split(',')[0].trim();
|
|
141
|
+
// For scoped: leading "@" then name then "@version"
|
|
142
|
+
if (first.startsWith('@')) {
|
|
143
|
+
const at = first.indexOf('@', 1);
|
|
144
|
+
if (at < 0)
|
|
145
|
+
return undefined;
|
|
146
|
+
return first.slice(0, at);
|
|
147
|
+
}
|
|
148
|
+
const at = first.indexOf('@');
|
|
149
|
+
if (at < 0)
|
|
150
|
+
return undefined;
|
|
151
|
+
return first.slice(0, at);
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=yarn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yarn.js","sourceRoot":"","sources":["../../../src/dependency/parsers/yarn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ParsedLockfile, ResolvedPackage } from './types';
|
|
2
|
+
export interface WorkspaceLockfiles {
|
|
3
|
+
npm: string[];
|
|
4
|
+
yarn: string[];
|
|
5
|
+
pnpm: string[];
|
|
6
|
+
pipenv: string[];
|
|
7
|
+
pip: string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Walk the workspace looking for known lockfiles. Skips `node_modules`,
|
|
11
|
+
* `.git`, `.venv`, `venv`, and `dist` to avoid duplicate hits from
|
|
12
|
+
* vendored deps.
|
|
13
|
+
*/
|
|
14
|
+
export declare function findLockfiles(workspaceRoot: string): WorkspaceLockfiles;
|
|
15
|
+
/** Parse a single lockfile path into a ParsedLockfile (or null). */
|
|
16
|
+
export declare function parseLockfile(lockfilePath: string): ParsedLockfile | null;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the full transitive dependency set across all parsed lockfiles.
|
|
19
|
+
*
|
|
20
|
+
* The "resolution" is essentially deduplication:
|
|
21
|
+
* - Same (ecosystem-family, name, version) seen in two lockfiles is reported once.
|
|
22
|
+
* - `family` collapses npm/yarn/pnpm into one (they share the npm registry)
|
|
23
|
+
* so a dep found in both package-lock.json and yarn.lock is not queried twice.
|
|
24
|
+
* - pip/pipenv are similarly collapsed into one PyPI family.
|
|
25
|
+
*
|
|
26
|
+
* Returns the flat list of resolved packages ready for OSV batching.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveAll(parsed: ParsedLockfile[]): ResolvedPackage[];
|
|
@@ -0,0 +1,159 @@
|
|
|
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.findLockfiles = findLockfiles;
|
|
37
|
+
exports.parseLockfile = parseLockfile;
|
|
38
|
+
exports.resolveAll = resolveAll;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const npm_1 = require("./parsers/npm");
|
|
42
|
+
const yarn_1 = require("./parsers/yarn");
|
|
43
|
+
const pnpm_1 = require("./parsers/pnpm");
|
|
44
|
+
const requirements_1 = require("./parsers/requirements");
|
|
45
|
+
const pipfile_1 = require("./parsers/pipfile");
|
|
46
|
+
/**
|
|
47
|
+
* The "resolver". For npm/yarn/pnpm/Pipfile.lock the lockfile ALREADY contains
|
|
48
|
+
* the fully-resolved transitive tree, so resolving = walking it (no network).
|
|
49
|
+
* For requirements.txt with no lockfile, entries are already individual; we
|
|
50
|
+
* just propagate `unresolved` flags so the checker can surface them instead
|
|
51
|
+
* of querying OSV with a version we don't actually know.
|
|
52
|
+
*
|
|
53
|
+
* This module is also responsible for finding lockfiles in the workspace.
|
|
54
|
+
*/
|
|
55
|
+
const LOCKFILE_NAMES = [
|
|
56
|
+
{ name: 'package-lock.json', ecosystem: 'npm' },
|
|
57
|
+
{ name: 'yarn.lock', ecosystem: 'yarn' },
|
|
58
|
+
{ name: 'pnpm-lock.yaml', ecosystem: 'pnpm' },
|
|
59
|
+
{ name: 'Pipfile.lock', ecosystem: 'pipenv' },
|
|
60
|
+
{ name: 'requirements.txt', ecosystem: 'pip' },
|
|
61
|
+
];
|
|
62
|
+
/**
|
|
63
|
+
* Walk the workspace looking for known lockfiles. Skips `node_modules`,
|
|
64
|
+
* `.git`, `.venv`, `venv`, and `dist` to avoid duplicate hits from
|
|
65
|
+
* vendored deps.
|
|
66
|
+
*/
|
|
67
|
+
function findLockfiles(workspaceRoot) {
|
|
68
|
+
const out = { npm: [], yarn: [], pnpm: [], pipenv: [], pip: [] };
|
|
69
|
+
const skip = new Set(['node_modules', '.git', '.venv', 'venv', 'env', 'dist', 'build', '.next', '.turbo']);
|
|
70
|
+
const walk = (dir, depth) => {
|
|
71
|
+
if (depth > 8)
|
|
72
|
+
return;
|
|
73
|
+
let entries;
|
|
74
|
+
try {
|
|
75
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
for (const e of entries) {
|
|
81
|
+
const full = path.join(dir, e.name);
|
|
82
|
+
if (e.isDirectory()) {
|
|
83
|
+
if (skip.has(e.name))
|
|
84
|
+
continue;
|
|
85
|
+
walk(full, depth + 1);
|
|
86
|
+
}
|
|
87
|
+
else if (e.isFile()) {
|
|
88
|
+
for (const lf of LOCKFILE_NAMES) {
|
|
89
|
+
if (e.name === lf.name) {
|
|
90
|
+
out[lf.ecosystem].push(full);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
walk(workspaceRoot, 0);
|
|
98
|
+
// De-duplicate by directory+ecosystem (a project should only have one
|
|
99
|
+
// lockfile per ecosystem per folder, but monorepos can have several).
|
|
100
|
+
const dedup = (arr) => Array.from(new Set(arr));
|
|
101
|
+
out.npm = dedup(out.npm);
|
|
102
|
+
out.yarn = dedup(out.yarn);
|
|
103
|
+
out.pnpm = dedup(out.pnpm);
|
|
104
|
+
out.pipenv = dedup(out.pipenv);
|
|
105
|
+
out.pip = dedup(out.pip);
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
/** Parse a single lockfile path into a ParsedLockfile (or null). */
|
|
109
|
+
function parseLockfile(lockfilePath) {
|
|
110
|
+
const base = path.basename(lockfilePath);
|
|
111
|
+
switch (base) {
|
|
112
|
+
case 'package-lock.json':
|
|
113
|
+
return (0, npm_1.parseNpmLockfile)(lockfilePath);
|
|
114
|
+
case 'yarn.lock':
|
|
115
|
+
return (0, yarn_1.parseYarnLockfile)(lockfilePath);
|
|
116
|
+
case 'pnpm-lock.yaml':
|
|
117
|
+
return (0, pnpm_1.parsePnpmLockfile)(lockfilePath);
|
|
118
|
+
case 'Pipfile.lock':
|
|
119
|
+
return (0, pipfile_1.parsePipfileLock)(lockfilePath);
|
|
120
|
+
case 'requirements.txt':
|
|
121
|
+
return (0, requirements_1.parseRequirementsTxt)(lockfilePath);
|
|
122
|
+
default:
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolve the full transitive dependency set across all parsed lockfiles.
|
|
128
|
+
*
|
|
129
|
+
* The "resolution" is essentially deduplication:
|
|
130
|
+
* - Same (ecosystem-family, name, version) seen in two lockfiles is reported once.
|
|
131
|
+
* - `family` collapses npm/yarn/pnpm into one (they share the npm registry)
|
|
132
|
+
* so a dep found in both package-lock.json and yarn.lock is not queried twice.
|
|
133
|
+
* - pip/pipenv are similarly collapsed into one PyPI family.
|
|
134
|
+
*
|
|
135
|
+
* Returns the flat list of resolved packages ready for OSV batching.
|
|
136
|
+
*/
|
|
137
|
+
function resolveAll(parsed) {
|
|
138
|
+
const familyOf = (e) => e === 'pip' || e === 'pipenv' ? 'pypi' : 'npm';
|
|
139
|
+
const seen = new Set();
|
|
140
|
+
const out = [];
|
|
141
|
+
for (const lf of parsed) {
|
|
142
|
+
if (!lf)
|
|
143
|
+
continue;
|
|
144
|
+
for (const pkg of lf.packages) {
|
|
145
|
+
// For npm-family, names are case-sensitive on disk but the registry
|
|
146
|
+
// treats them case-insensitively for lookup; normalize to lower for
|
|
147
|
+
// dedup so we don't double-query OSV.
|
|
148
|
+
const fam = familyOf(pkg.ecosystem);
|
|
149
|
+
const nameKey = fam === 'npm' ? pkg.name : pkg.name.toLowerCase();
|
|
150
|
+
const id = `${fam}:${nameKey}@${pkg.version || 'unknown'}`;
|
|
151
|
+
if (seen.has(id))
|
|
152
|
+
continue;
|
|
153
|
+
seen.add(id);
|
|
154
|
+
out.push(pkg);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/dependency/resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the dependency vulnerability checker (Phase 2).
|
|
3
|
+
*
|
|
4
|
+
* No source code is ever sent anywhere — only package name + version strings
|
|
5
|
+
* leave the machine, to OSV.dev (free, no key) and GitHub Advisory REST API
|
|
6
|
+
* (free, no key for 60 req/hr; 5000 req/hr with optional token). NVD is an
|
|
7
|
+
* optional CVSS fallback.
|
|
8
|
+
*/
|
|
9
|
+
/** Ecosystems we can parse a lockfile for. */
|
|
10
|
+
export type Ecosystem = 'npm' | 'yarn' | 'pnpm' | 'pip' | 'pipenv';
|
|
11
|
+
/** OSV ecosystem identifier used in OSV.dev API payloads. */
|
|
12
|
+
export type OsvEcosystem = 'npm' | 'PyPI';
|
|
13
|
+
/**
|
|
14
|
+
* A single resolved dependency as extracted from a lockfile.
|
|
15
|
+
* For npm/yarn/pnpm this is a transitive entry straight out of the lockfile
|
|
16
|
+
* (so the resolver does not need to hit the network). For requirements.txt
|
|
17
|
+
* without a Pipfile.lock, entries may be marked `unresolved` instead of
|
|
18
|
+
* fetching a registry.
|
|
19
|
+
*/
|
|
20
|
+
export interface ResolvedPackage {
|
|
21
|
+
ecosystem: Ecosystem;
|
|
22
|
+
/** Package name as the registry knows it (PyPI names are normalized to lower). */
|
|
23
|
+
name: string;
|
|
24
|
+
/** Resolved version (semver for JS, PEP 440-ish for Python). */
|
|
25
|
+
version: string;
|
|
26
|
+
/** SPDX license identifier when available from lockfile/metadata, else undefined. */
|
|
27
|
+
license?: string;
|
|
28
|
+
/** Path to the manifest this entry came from (for Apply affordance). */
|
|
29
|
+
manifestPath?: string;
|
|
30
|
+
/**
|
|
31
|
+
* True when the version could not be resolved locally (e.g. requirements.txt
|
|
32
|
+
* pin like `>=1.0` with no lockfile). These are reported as "unresolved"
|
|
33
|
+
* findings rather than queried against OSV (since we don't know the version).
|
|
34
|
+
*/
|
|
35
|
+
unresolved?: boolean;
|
|
36
|
+
/** Direct dependency (declared in manifest) vs transitive (resolved from lockfile). */
|
|
37
|
+
direct?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/** Output of a lockfile parser. */
|
|
40
|
+
export interface ParsedLockfile {
|
|
41
|
+
ecosystem: Ecosystem;
|
|
42
|
+
/** Path to the lockfile that was parsed. */
|
|
43
|
+
lockfilePath: string;
|
|
44
|
+
/** Path to the manifest the lockfile corresponds to (package.json, requirements.txt, ...). */
|
|
45
|
+
manifestPath?: string;
|
|
46
|
+
/** All resolved packages, direct + transitive. */
|
|
47
|
+
packages: ResolvedPackage[];
|
|
48
|
+
/** SPDX license of the project itself, if discoverable (npm package.json `license`). */
|
|
49
|
+
projectLicense?: string;
|
|
50
|
+
}
|
|
51
|
+
/** An affected version range as returned by OSV / GHSA. */
|
|
52
|
+
export interface AffectedRange {
|
|
53
|
+
/** OSV ecosystem ('npm' | 'PyPI'). */
|
|
54
|
+
ecosystem: OsvEcosystem;
|
|
55
|
+
/** Lower bound of the affected range, inclusive (optional). */
|
|
56
|
+
introduced?: string;
|
|
57
|
+
/** Upper bound of the affected range, exclusive (the "first fixed" version). */
|
|
58
|
+
fixed?: string;
|
|
59
|
+
/** Raw range string for diagnostics. */
|
|
60
|
+
raw?: string;
|
|
61
|
+
}
|
|
62
|
+
/** A vulnerability record normalized from OSV / GHSA / NVD. */
|
|
63
|
+
export interface Vulnerability {
|
|
64
|
+
/** OSV id (OSV-...) or GHSA id (GHSA-...) or CVE id (CVE-...). */
|
|
65
|
+
id: string;
|
|
66
|
+
/** Source the record came from. */
|
|
67
|
+
source: 'osv' | 'ghsa' | 'nvd';
|
|
68
|
+
/** One-line summary. */
|
|
69
|
+
summary: string;
|
|
70
|
+
/** List of affected version ranges. */
|
|
71
|
+
affected: AffectedRange[];
|
|
72
|
+
/** CVSS v3 base score 0-10 when known (NVD or OSV severity). */
|
|
73
|
+
cvssScore?: number;
|
|
74
|
+
/** URLs for the user to read more. */
|
|
75
|
+
references?: string[];
|
|
76
|
+
}
|
|
77
|
+
/** A vulnerability matched to a resolved package. */
|
|
78
|
+
export interface MatchedVulnerability {
|
|
79
|
+
/** The resolved package that is vulnerable. */
|
|
80
|
+
package: ResolvedPackage;
|
|
81
|
+
/** Vulnerabilities affecting `package.version`. */
|
|
82
|
+
vulnerabilities: Vulnerability[];
|
|
83
|
+
/** Smallest version bump that exits all affected ranges, when computable. */
|
|
84
|
+
minSafeVersion?: string;
|
|
85
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared types for the dependency vulnerability checker (Phase 2).
|
|
4
|
+
*
|
|
5
|
+
* No source code is ever sent anywhere — only package name + version strings
|
|
6
|
+
* leave the machine, to OSV.dev (free, no key) and GitHub Advisory REST API
|
|
7
|
+
* (free, no key for 60 req/hr; 5000 req/hr with optional token). NVD is an
|
|
8
|
+
* optional CVSS fallback.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/dependency/types.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -40,6 +40,7 @@ const scan_1 = require("../tools/scan");
|
|
|
40
40
|
const map_1 = require("../tools/map");
|
|
41
41
|
const fix_1 = require("../tools/fix");
|
|
42
42
|
const attack_1 = require("../tools/attack");
|
|
43
|
+
const scanDependencies_1 = require("../tools/scanDependencies");
|
|
43
44
|
const PROTOCOL_VERSION = '2025-03-26';
|
|
44
45
|
const SERVER_NAME = 'securecode-mcp';
|
|
45
46
|
const SERVER_VERSION = '0.2.0';
|
|
@@ -68,6 +69,7 @@ const TOOL_HANDLERS = {
|
|
|
68
69
|
'securecode.map': map_1.toolMap,
|
|
69
70
|
'securecode.fix': fix_1.toolFix,
|
|
70
71
|
'securecode.attack': attack_1.toolAttack,
|
|
72
|
+
'securecode.scan-dependencies': scanDependencies_1.toolScanDependencies,
|
|
71
73
|
};
|
|
72
74
|
function validateArgs(tool, args) {
|
|
73
75
|
const schema = tool.inputSchema;
|