@sentinel-atl/scanner 0.3.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 +104 -0
- package/dist/dependency-scanner.d.ts +22 -0
- package/dist/dependency-scanner.d.ts.map +1 -0
- package/dist/dependency-scanner.js +70 -0
- package/dist/dependency-scanner.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/package-resolver.d.ts +30 -0
- package/dist/package-resolver.d.ts.map +1 -0
- package/dist/package-resolver.js +105 -0
- package/dist/package-resolver.js.map +1 -0
- package/dist/pattern-scanner.d.ts +30 -0
- package/dist/pattern-scanner.d.ts.map +1 -0
- package/dist/pattern-scanner.js +178 -0
- package/dist/pattern-scanner.js.map +1 -0
- package/dist/permission-scanner.d.ts +28 -0
- package/dist/permission-scanner.d.ts.map +1 -0
- package/dist/permission-scanner.js +100 -0
- package/dist/permission-scanner.js.map +1 -0
- package/dist/publisher-scanner.d.ts +56 -0
- package/dist/publisher-scanner.d.ts.map +1 -0
- package/dist/publisher-scanner.js +238 -0
- package/dist/publisher-scanner.js.map +1 -0
- package/dist/scanner.d.ts +61 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +71 -0
- package/dist/scanner.js.map +1 -0
- package/dist/stc.d.ts +88 -0
- package/dist/stc.d.ts.map +1 -0
- package/dist/stc.js +115 -0
- package/dist/stc.js.map +1 -0
- package/dist/tool-prober.d.ts +46 -0
- package/dist/tool-prober.d.ts.map +1 -0
- package/dist/tool-prober.js +158 -0
- package/dist/tool-prober.js.map +1 -0
- package/dist/trust-score.d.ts +26 -0
- package/dist/trust-score.d.ts.map +1 -0
- package/dist/trust-score.js +65 -0
- package/dist/trust-score.js.map +1 -0
- package/package.json +52 -0
package/dist/stc.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel Trust Certificate (STC) — A signed, verifiable scan result.
|
|
3
|
+
*
|
|
4
|
+
* An STC is a cryptographically signed certificate that attests to the
|
|
5
|
+
* security posture of an MCP server package at a specific point in time.
|
|
6
|
+
*
|
|
7
|
+
* Think of it as:
|
|
8
|
+
* - SSL certificate meets npm audit report
|
|
9
|
+
* - A "nutrition label" for MCP servers
|
|
10
|
+
* - Machine-readable trust evidence
|
|
11
|
+
*
|
|
12
|
+
* Format:
|
|
13
|
+
* {
|
|
14
|
+
* "@context": "https://sentinel.trust/stc/v1",
|
|
15
|
+
* "type": "SentinelTrustCertificate",
|
|
16
|
+
* "id": "stc:<hash>",
|
|
17
|
+
* "issuedAt": ISO timestamp,
|
|
18
|
+
* "expiresAt": ISO timestamp,
|
|
19
|
+
* "issuer": { did, name },
|
|
20
|
+
* "subject": { packageName, packageVersion, codeHash },
|
|
21
|
+
* "trustScore": { overall, grade, breakdown },
|
|
22
|
+
* "findings": { critical, high, medium, low, info },
|
|
23
|
+
* "permissions": [...],
|
|
24
|
+
* "proof": { type: "Ed25519Signature2024", ... }
|
|
25
|
+
* }
|
|
26
|
+
*/
|
|
27
|
+
import { type KeyProvider } from '@sentinel-atl/core';
|
|
28
|
+
import type { ScanReport, TrustScore } from './scanner.js';
|
|
29
|
+
export interface STCIssuer {
|
|
30
|
+
did: string;
|
|
31
|
+
name?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface STCSubject {
|
|
34
|
+
packageName: string;
|
|
35
|
+
packageVersion: string;
|
|
36
|
+
codeHash: string;
|
|
37
|
+
}
|
|
38
|
+
export interface STCFindingSummary {
|
|
39
|
+
critical: number;
|
|
40
|
+
high: number;
|
|
41
|
+
medium: number;
|
|
42
|
+
low: number;
|
|
43
|
+
info: number;
|
|
44
|
+
total: number;
|
|
45
|
+
}
|
|
46
|
+
export interface STCProof {
|
|
47
|
+
type: 'Ed25519Signature2024';
|
|
48
|
+
created: string;
|
|
49
|
+
verificationMethod: string;
|
|
50
|
+
signature: string;
|
|
51
|
+
}
|
|
52
|
+
export interface SentinelTrustCertificate {
|
|
53
|
+
'@context': 'https://sentinel.trust/stc/v1';
|
|
54
|
+
type: 'SentinelTrustCertificate';
|
|
55
|
+
id: string;
|
|
56
|
+
issuedAt: string;
|
|
57
|
+
expiresAt: string;
|
|
58
|
+
issuer: STCIssuer;
|
|
59
|
+
subject: STCSubject;
|
|
60
|
+
trustScore: TrustScore;
|
|
61
|
+
findingSummary: STCFindingSummary;
|
|
62
|
+
permissions: string[];
|
|
63
|
+
scannerVersion: string;
|
|
64
|
+
proof: STCProof;
|
|
65
|
+
}
|
|
66
|
+
export interface STCVerifyResult {
|
|
67
|
+
valid: boolean;
|
|
68
|
+
error?: string;
|
|
69
|
+
certificate?: SentinelTrustCertificate;
|
|
70
|
+
}
|
|
71
|
+
export interface IssueSTCOptions {
|
|
72
|
+
scanReport: ScanReport;
|
|
73
|
+
codeHash: string;
|
|
74
|
+
issuerDid: string;
|
|
75
|
+
issuerKeyId: string;
|
|
76
|
+
issuerName?: string;
|
|
77
|
+
/** Certificate validity in hours (default: 720 = 30 days) */
|
|
78
|
+
validityHours?: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Issue a Sentinel Trust Certificate from a scan report.
|
|
82
|
+
*/
|
|
83
|
+
export declare function issueSTC(keyProvider: KeyProvider, options: IssueSTCOptions): Promise<SentinelTrustCertificate>;
|
|
84
|
+
/**
|
|
85
|
+
* Verify a Sentinel Trust Certificate's signature and validity.
|
|
86
|
+
*/
|
|
87
|
+
export declare function verifySTC(certificate: SentinelTrustCertificate): Promise<STCVerifyResult>;
|
|
88
|
+
//# sourceMappingURL=stc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stc.d.ts","sourceRoot":"","sources":["../src/stc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EACL,KAAK,WAAW,EASjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI3D,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,+BAA+B,CAAC;IAC5C,IAAI,EAAE,0BAA0B,CAAC;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,UAAU,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,cAAc,EAAE,iBAAiB,CAAC;IAClC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,wBAAwB,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAID;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,wBAAwB,CAAC,CAyDnC;AAID;;GAEG;AACH,wBAAsB,SAAS,CAC7B,WAAW,EAAE,wBAAwB,GACpC,OAAO,CAAC,eAAe,CAAC,CAwB1B"}
|
package/dist/stc.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel Trust Certificate (STC) — A signed, verifiable scan result.
|
|
3
|
+
*
|
|
4
|
+
* An STC is a cryptographically signed certificate that attests to the
|
|
5
|
+
* security posture of an MCP server package at a specific point in time.
|
|
6
|
+
*
|
|
7
|
+
* Think of it as:
|
|
8
|
+
* - SSL certificate meets npm audit report
|
|
9
|
+
* - A "nutrition label" for MCP servers
|
|
10
|
+
* - Machine-readable trust evidence
|
|
11
|
+
*
|
|
12
|
+
* Format:
|
|
13
|
+
* {
|
|
14
|
+
* "@context": "https://sentinel.trust/stc/v1",
|
|
15
|
+
* "type": "SentinelTrustCertificate",
|
|
16
|
+
* "id": "stc:<hash>",
|
|
17
|
+
* "issuedAt": ISO timestamp,
|
|
18
|
+
* "expiresAt": ISO timestamp,
|
|
19
|
+
* "issuer": { did, name },
|
|
20
|
+
* "subject": { packageName, packageVersion, codeHash },
|
|
21
|
+
* "trustScore": { overall, grade, breakdown },
|
|
22
|
+
* "findings": { critical, high, medium, low, info },
|
|
23
|
+
* "permissions": [...],
|
|
24
|
+
* "proof": { type: "Ed25519Signature2024", ... }
|
|
25
|
+
* }
|
|
26
|
+
*/
|
|
27
|
+
import { verify, toBase64Url, fromBase64Url, textToBytes, toHex, hash, didToPublicKey, } from '@sentinel-atl/core';
|
|
28
|
+
// ─── STC Issuance ────────────────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Issue a Sentinel Trust Certificate from a scan report.
|
|
31
|
+
*/
|
|
32
|
+
export async function issueSTC(keyProvider, options) {
|
|
33
|
+
const { scanReport, codeHash, issuerDid, issuerKeyId, issuerName, validityHours = 720, } = options;
|
|
34
|
+
const now = new Date();
|
|
35
|
+
const expiresAt = new Date(now.getTime() + validityHours * 3600_000);
|
|
36
|
+
// Count findings by severity
|
|
37
|
+
const findingSummary = {
|
|
38
|
+
critical: 0, high: 0, medium: 0, low: 0, info: 0, total: scanReport.findings.length,
|
|
39
|
+
};
|
|
40
|
+
for (const f of scanReport.findings) {
|
|
41
|
+
findingSummary[f.severity]++;
|
|
42
|
+
}
|
|
43
|
+
// Build unsigned certificate body
|
|
44
|
+
const body = {
|
|
45
|
+
'@context': 'https://sentinel.trust/stc/v1',
|
|
46
|
+
type: 'SentinelTrustCertificate',
|
|
47
|
+
id: '', // computed after hashing
|
|
48
|
+
issuedAt: now.toISOString(),
|
|
49
|
+
expiresAt: expiresAt.toISOString(),
|
|
50
|
+
issuer: { did: issuerDid, name: issuerName },
|
|
51
|
+
subject: {
|
|
52
|
+
packageName: scanReport.packageName,
|
|
53
|
+
packageVersion: scanReport.packageVersion,
|
|
54
|
+
codeHash,
|
|
55
|
+
},
|
|
56
|
+
trustScore: scanReport.trustScore,
|
|
57
|
+
findingSummary,
|
|
58
|
+
permissions: scanReport.permissions.kinds,
|
|
59
|
+
scannerVersion: scanReport.scannerVersion,
|
|
60
|
+
};
|
|
61
|
+
// Generate deterministic ID from body hash
|
|
62
|
+
const bodyBytes = textToBytes(JSON.stringify(sortDeep(body)));
|
|
63
|
+
const bodyHash = toHex(hash(bodyBytes));
|
|
64
|
+
body.id = `stc:${bodyHash.slice(0, 16)}`;
|
|
65
|
+
// Sign the canonical body
|
|
66
|
+
const canonicalBytes = textToBytes(JSON.stringify(sortDeep(body)));
|
|
67
|
+
const sig = await keyProvider.sign(issuerKeyId, canonicalBytes);
|
|
68
|
+
const proof = {
|
|
69
|
+
type: 'Ed25519Signature2024',
|
|
70
|
+
created: now.toISOString(),
|
|
71
|
+
verificationMethod: `${issuerDid}#key-0`,
|
|
72
|
+
signature: toBase64Url(sig),
|
|
73
|
+
};
|
|
74
|
+
return { ...body, proof };
|
|
75
|
+
}
|
|
76
|
+
// ─── STC Verification ────────────────────────────────────────────────
|
|
77
|
+
/**
|
|
78
|
+
* Verify a Sentinel Trust Certificate's signature and validity.
|
|
79
|
+
*/
|
|
80
|
+
export async function verifySTC(certificate) {
|
|
81
|
+
try {
|
|
82
|
+
// Check expiry
|
|
83
|
+
if (new Date(certificate.expiresAt) < new Date()) {
|
|
84
|
+
return { valid: false, error: 'Certificate has expired' };
|
|
85
|
+
}
|
|
86
|
+
// Extract the body (everything except proof)
|
|
87
|
+
const { proof, ...body } = certificate;
|
|
88
|
+
// Reconstruct canonical form and verify signature
|
|
89
|
+
const canonicalBytes = textToBytes(JSON.stringify(sortDeep(body)));
|
|
90
|
+
const sigBytes = fromBase64Url(proof.signature);
|
|
91
|
+
const publicKey = didToPublicKey(certificate.issuer.did);
|
|
92
|
+
const isValid = await verify(sigBytes, canonicalBytes, publicKey);
|
|
93
|
+
if (!isValid) {
|
|
94
|
+
return { valid: false, error: 'Invalid certificate signature' };
|
|
95
|
+
}
|
|
96
|
+
return { valid: true, certificate };
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
return { valid: false, error: `Verification failed: ${e.message}` };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// ─── Helpers ─────────────────────────────────────────────────────────
|
|
103
|
+
function sortDeep(value) {
|
|
104
|
+
if (Array.isArray(value))
|
|
105
|
+
return value.map(sortDeep);
|
|
106
|
+
if (value !== null && typeof value === 'object') {
|
|
107
|
+
const sorted = {};
|
|
108
|
+
for (const key of Object.keys(value).sort()) {
|
|
109
|
+
sorted[key] = sortDeep(value[key]);
|
|
110
|
+
}
|
|
111
|
+
return sorted;
|
|
112
|
+
}
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=stc.js.map
|
package/dist/stc.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stc.js","sourceRoot":"","sources":["../src/stc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool Prober — connects to an MCP server and discovers its tool declarations.
|
|
3
|
+
*
|
|
4
|
+
* Starts the server in a sandboxed subprocess and calls `tools/list`
|
|
5
|
+
* to enumerate what tools the server exposes. This is runtime analysis
|
|
6
|
+
* that complements the static code scanning.
|
|
7
|
+
*/
|
|
8
|
+
import type { Finding } from './scanner.js';
|
|
9
|
+
export interface MCPTool {
|
|
10
|
+
name: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
inputSchema?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
export interface ToolProbeResult {
|
|
15
|
+
/** Whether the probe succeeded */
|
|
16
|
+
success: boolean;
|
|
17
|
+
/** Error message if probe failed */
|
|
18
|
+
error?: string;
|
|
19
|
+
/** Server name from initialize response */
|
|
20
|
+
serverName?: string;
|
|
21
|
+
/** Server version from initialize response */
|
|
22
|
+
serverVersion?: string;
|
|
23
|
+
/** Discovered tools */
|
|
24
|
+
tools: MCPTool[];
|
|
25
|
+
/** Findings from tool analysis */
|
|
26
|
+
findings: Finding[];
|
|
27
|
+
/** Probe duration in ms */
|
|
28
|
+
durationMs: number;
|
|
29
|
+
}
|
|
30
|
+
export interface ProbeOptions {
|
|
31
|
+
/** Command to start the MCP server (e.g., "node dist/index.js") */
|
|
32
|
+
command: string;
|
|
33
|
+
/** Arguments to pass to the command */
|
|
34
|
+
args?: string[];
|
|
35
|
+
/** Working directory */
|
|
36
|
+
cwd?: string;
|
|
37
|
+
/** Timeout for the entire probe in ms (default: 15000) */
|
|
38
|
+
timeoutMs?: number;
|
|
39
|
+
/** Environment variables to set */
|
|
40
|
+
env?: Record<string, string>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Probe an MCP server to discover its tools.
|
|
44
|
+
*/
|
|
45
|
+
export declare function probeTools(options: ProbeOptions): Promise<ToolProbeResult>;
|
|
46
|
+
//# sourceMappingURL=tool-prober.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-prober.d.ts","sourceRoot":"","sources":["../src/tool-prober.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAI5C,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,eAAe;IAC9B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uBAAuB;IACvB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,kCAAkC;IAClC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAoBD;;GAEG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CA4IhF"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool Prober — connects to an MCP server and discovers its tool declarations.
|
|
3
|
+
*
|
|
4
|
+
* Starts the server in a sandboxed subprocess and calls `tools/list`
|
|
5
|
+
* to enumerate what tools the server exposes. This is runtime analysis
|
|
6
|
+
* that complements the static code scanning.
|
|
7
|
+
*/
|
|
8
|
+
import { spawn } from 'node:child_process';
|
|
9
|
+
import { createInterface } from 'node:readline';
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
// ─── Suspicious tool patterns ─────────────────────────────────────────
|
|
12
|
+
const SUSPICIOUS_TOOL_NAMES = [
|
|
13
|
+
/exec/i, /shell/i, /command/i, /run_code/i, /system/i,
|
|
14
|
+
/eval/i, /upload/i, /download/i, /delete_all/i, /drop/i,
|
|
15
|
+
/admin/i, /root/i, /sudo/i, /install/i, /uninstall/i,
|
|
16
|
+
];
|
|
17
|
+
const DANGEROUS_TOOL_DESCRIPTIONS = [
|
|
18
|
+
/execut(?:e|ing)\s+(?:arbitrary|any|shell|system)/i,
|
|
19
|
+
/run\s+(?:any|arbitrary|shell)/i,
|
|
20
|
+
/delete\s+(?:all|everything|any)/i,
|
|
21
|
+
/access\s+(?:all|any)\s+files/i,
|
|
22
|
+
/modify\s+system/i,
|
|
23
|
+
];
|
|
24
|
+
// ─── Prober ───────────────────────────────────────────────────────────
|
|
25
|
+
/**
|
|
26
|
+
* Probe an MCP server to discover its tools.
|
|
27
|
+
*/
|
|
28
|
+
export async function probeTools(options) {
|
|
29
|
+
const start = Date.now();
|
|
30
|
+
const timeoutMs = options.timeoutMs ?? 15_000;
|
|
31
|
+
const findings = [];
|
|
32
|
+
let child = null;
|
|
33
|
+
try {
|
|
34
|
+
// Start the server process
|
|
35
|
+
child = spawn(options.command, options.args ?? [], {
|
|
36
|
+
cwd: options.cwd,
|
|
37
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
38
|
+
env: { ...process.env, ...options.env },
|
|
39
|
+
timeout: timeoutMs,
|
|
40
|
+
});
|
|
41
|
+
const reader = createInterface({ input: child.stdout });
|
|
42
|
+
// Helper: send JSON-RPC message and wait for response
|
|
43
|
+
const sendRequest = (method, params) => {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const id = randomUUID();
|
|
46
|
+
const msg = JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n';
|
|
47
|
+
const timeout = setTimeout(() => {
|
|
48
|
+
cleanup();
|
|
49
|
+
reject(new Error(`Request timed out: ${method}`));
|
|
50
|
+
}, Math.min(timeoutMs, 10_000));
|
|
51
|
+
const onLine = (line) => {
|
|
52
|
+
try {
|
|
53
|
+
const resp = JSON.parse(line);
|
|
54
|
+
if (resp.id === id) {
|
|
55
|
+
cleanup();
|
|
56
|
+
if (resp.error) {
|
|
57
|
+
reject(new Error(resp.error.message ?? 'RPC error'));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
resolve(resp.result);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Not our response
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const cleanup = () => {
|
|
69
|
+
clearTimeout(timeout);
|
|
70
|
+
reader.removeListener('line', onLine);
|
|
71
|
+
};
|
|
72
|
+
reader.on('line', onLine);
|
|
73
|
+
child.stdin.write(msg);
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
// Step 1: Initialize
|
|
77
|
+
const initResult = await sendRequest('initialize', {
|
|
78
|
+
protocolVersion: '2024-11-05',
|
|
79
|
+
capabilities: {},
|
|
80
|
+
clientInfo: { name: 'sentinel-prober', version: '0.3.0' },
|
|
81
|
+
});
|
|
82
|
+
const serverName = initResult?.serverInfo?.name;
|
|
83
|
+
const serverVersion = initResult?.serverInfo?.version;
|
|
84
|
+
// Send initialized notification
|
|
85
|
+
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized' }) + '\n');
|
|
86
|
+
// Step 2: List tools
|
|
87
|
+
const toolsResult = await sendRequest('tools/list', {});
|
|
88
|
+
const tools = (toolsResult?.tools ?? []).map((t) => ({
|
|
89
|
+
name: t.name,
|
|
90
|
+
description: t.description,
|
|
91
|
+
inputSchema: t.inputSchema,
|
|
92
|
+
}));
|
|
93
|
+
// Step 3: Analyze tool declarations for suspicious patterns
|
|
94
|
+
for (const tool of tools) {
|
|
95
|
+
for (const pattern of SUSPICIOUS_TOOL_NAMES) {
|
|
96
|
+
if (pattern.test(tool.name)) {
|
|
97
|
+
findings.push({
|
|
98
|
+
severity: 'high',
|
|
99
|
+
category: 'dangerous-pattern',
|
|
100
|
+
title: `Suspicious tool name: "${tool.name}"`,
|
|
101
|
+
description: `Tool "${tool.name}" has a name matching a dangerous pattern (${pattern.source})`,
|
|
102
|
+
});
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (tool.description) {
|
|
107
|
+
for (const pattern of DANGEROUS_TOOL_DESCRIPTIONS) {
|
|
108
|
+
if (pattern.test(tool.description)) {
|
|
109
|
+
findings.push({
|
|
110
|
+
severity: 'high',
|
|
111
|
+
category: 'dangerous-pattern',
|
|
112
|
+
title: `Dangerous tool description: "${tool.name}"`,
|
|
113
|
+
description: `Tool "${tool.name}" description suggests dangerous capabilities: ${tool.description.slice(0, 100)}`,
|
|
114
|
+
});
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Many tools is itself a code smell
|
|
121
|
+
if (tools.length > 50) {
|
|
122
|
+
findings.push({
|
|
123
|
+
severity: 'medium',
|
|
124
|
+
category: 'dangerous-pattern',
|
|
125
|
+
title: `Excessive tool count: ${tools.length} tools`,
|
|
126
|
+
description: 'Servers with many tools may have an overly broad attack surface',
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
success: true,
|
|
131
|
+
serverName,
|
|
132
|
+
serverVersion,
|
|
133
|
+
tools,
|
|
134
|
+
findings,
|
|
135
|
+
durationMs: Date.now() - start,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
return {
|
|
140
|
+
success: false,
|
|
141
|
+
error: err.message,
|
|
142
|
+
tools: [],
|
|
143
|
+
findings,
|
|
144
|
+
durationMs: Date.now() - start,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
if (child && !child.killed) {
|
|
149
|
+
child.kill('SIGTERM');
|
|
150
|
+
// Force kill after 2s
|
|
151
|
+
setTimeout(() => {
|
|
152
|
+
if (child && !child.killed)
|
|
153
|
+
child.kill('SIGKILL');
|
|
154
|
+
}, 2000);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=tool-prober.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-prober.js","sourceRoot":"","sources":["../src/tool-prober.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trust score computation — converts raw findings into a 0-100 score.
|
|
3
|
+
*
|
|
4
|
+
* Weights:
|
|
5
|
+
* - Dependencies (25%): vulnerability count and severity
|
|
6
|
+
* - Code-Patterns (30%): dangerous patterns and obfuscation
|
|
7
|
+
* - Permissions (25%): scope of system access
|
|
8
|
+
* - Publisher (20%): npm registry identity verification
|
|
9
|
+
*/
|
|
10
|
+
import type { Finding } from './scanner.js';
|
|
11
|
+
import type { DependencyScanResult } from './dependency-scanner.js';
|
|
12
|
+
import type { PatternScanResult } from './pattern-scanner.js';
|
|
13
|
+
import type { PermissionScanResult } from './permission-scanner.js';
|
|
14
|
+
import type { PublisherScanResult } from './publisher-scanner.js';
|
|
15
|
+
export interface ScoreBreakdown {
|
|
16
|
+
/** 0-100 score for dependency health */
|
|
17
|
+
dependencies: number;
|
|
18
|
+
/** 0-100 score for code patterns */
|
|
19
|
+
codePatterns: number;
|
|
20
|
+
/** 0-100 score for permission scope */
|
|
21
|
+
permissions: number;
|
|
22
|
+
/** 0-100 score for publisher identity */
|
|
23
|
+
publisher: number;
|
|
24
|
+
}
|
|
25
|
+
export declare function computeTrustScore(findings: Finding[], deps: DependencyScanResult, patterns: PatternScanResult, permissions: PermissionScanResult, publisher?: PublisherScanResult): ScoreBreakdown;
|
|
26
|
+
//# sourceMappingURL=trust-score.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trust-score.d.ts","sourceRoot":"","sources":["../src/trust-score.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAElE,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;CACnB;AAUD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,OAAO,EAAE,EACnB,IAAI,EAAE,oBAAoB,EAC1B,QAAQ,EAAE,iBAAiB,EAC3B,WAAW,EAAE,oBAAoB,EACjC,SAAS,CAAC,EAAE,mBAAmB,GAC9B,cAAc,CAOhB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trust score computation — converts raw findings into a 0-100 score.
|
|
3
|
+
*
|
|
4
|
+
* Weights:
|
|
5
|
+
* - Dependencies (25%): vulnerability count and severity
|
|
6
|
+
* - Code-Patterns (30%): dangerous patterns and obfuscation
|
|
7
|
+
* - Permissions (25%): scope of system access
|
|
8
|
+
* - Publisher (20%): npm registry identity verification
|
|
9
|
+
*/
|
|
10
|
+
const SEVERITY_PENALTIES = {
|
|
11
|
+
critical: 30,
|
|
12
|
+
high: 20,
|
|
13
|
+
medium: 10,
|
|
14
|
+
low: 5,
|
|
15
|
+
info: 1,
|
|
16
|
+
};
|
|
17
|
+
export function computeTrustScore(findings, deps, patterns, permissions, publisher) {
|
|
18
|
+
return {
|
|
19
|
+
dependencies: computeDependencyScore(deps),
|
|
20
|
+
codePatterns: computePatternScore(patterns),
|
|
21
|
+
permissions: computePermissionScore(permissions),
|
|
22
|
+
publisher: publisher?.score ?? 50,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function computeDependencyScore(deps) {
|
|
26
|
+
if (deps.vulnerabilities.length === 0)
|
|
27
|
+
return 100;
|
|
28
|
+
let penalty = 0;
|
|
29
|
+
for (const vuln of deps.vulnerabilities) {
|
|
30
|
+
const sev = vuln.severity === 'moderate' ? 'medium' : vuln.severity;
|
|
31
|
+
penalty += SEVERITY_PENALTIES[sev] ?? 5;
|
|
32
|
+
}
|
|
33
|
+
return Math.max(0, 100 - penalty);
|
|
34
|
+
}
|
|
35
|
+
function computePatternScore(patterns) {
|
|
36
|
+
if (patterns.findings.length === 0)
|
|
37
|
+
return 100;
|
|
38
|
+
let penalty = 0;
|
|
39
|
+
for (const finding of patterns.findings) {
|
|
40
|
+
penalty += SEVERITY_PENALTIES[finding.severity] ?? 5;
|
|
41
|
+
}
|
|
42
|
+
return Math.max(0, 100 - penalty);
|
|
43
|
+
}
|
|
44
|
+
function computePermissionScore(permissions) {
|
|
45
|
+
// Base score starts at 100, penalize for each permission kind + detections
|
|
46
|
+
let penalty = 0;
|
|
47
|
+
const kindPenalties = {
|
|
48
|
+
process: 25,
|
|
49
|
+
network: 15,
|
|
50
|
+
native: 20,
|
|
51
|
+
filesystem: 10,
|
|
52
|
+
environment: 5,
|
|
53
|
+
crypto: 0, // crypto is usually fine
|
|
54
|
+
};
|
|
55
|
+
for (const kind of permissions.kinds) {
|
|
56
|
+
penalty += kindPenalties[kind] ?? 5;
|
|
57
|
+
}
|
|
58
|
+
// Additional penalty for high number of detections
|
|
59
|
+
if (permissions.detections.length > 20)
|
|
60
|
+
penalty += 10;
|
|
61
|
+
if (permissions.detections.length > 50)
|
|
62
|
+
penalty += 10;
|
|
63
|
+
return Math.max(0, 100 - penalty);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=trust-score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trust-score.js","sourceRoot":"","sources":["../src/trust-score.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAmBH,MAAM,kBAAkB,GAA2B;IACjD,QAAQ,EAAE,EAAE;IACZ,IAAI,EAAE,EAAE;IACR,MAAM,EAAE,EAAE;IACV,GAAG,EAAE,CAAC;IACN,IAAI,EAAE,CAAC;CACR,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAC/B,QAAmB,EACnB,IAA0B,EAC1B,QAA2B,EAC3B,WAAiC,EACjC,SAA+B;IAE/B,OAAO;QACL,YAAY,EAAE,sBAAsB,CAAC,IAAI,CAAC;QAC1C,YAAY,EAAE,mBAAmB,CAAC,QAAQ,CAAC;QAC3C,WAAW,EAAE,sBAAsB,CAAC,WAAW,CAAC;QAChD,SAAS,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE;KAClC,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAAC,IAA0B;IACxD,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAElD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC;QACpE,OAAO,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,mBAAmB,CAAC,QAA2B;IACtD,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAE/C,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACxC,OAAO,IAAI,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,sBAAsB,CAAC,WAAiC;IAC/D,2EAA2E;IAC3E,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,MAAM,aAAa,GAA2B;QAC5C,OAAO,EAAE,EAAE;QACX,OAAO,EAAE,EAAE;QACX,MAAM,EAAE,EAAE;QACV,UAAU,EAAE,EAAE;QACd,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,CAAC,EAAE,yBAAyB;KACrC,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;QACrC,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,mDAAmD;IACnD,IAAI,WAAW,CAAC,UAAU,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,EAAE,CAAC;IACtD,IAAI,WAAW,CAAC,UAAU,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,EAAE,CAAC;IAEtD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;AACpC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sentinel-atl/scanner",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "MCP server security scanner — npm audit meets AI agent trust verification",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"lint": "tsc --noEmit",
|
|
18
|
+
"clean": "rm -rf dist"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@sentinel-atl/core": "*",
|
|
22
|
+
"@sentinel-atl/attestation": "*"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"vitest": "^3.2.4",
|
|
26
|
+
"typescript": "^5.7.0",
|
|
27
|
+
"@types/node": "^20.0.0"
|
|
28
|
+
},
|
|
29
|
+
"license": "Apache-2.0",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/sentinel-atl/project-sentinel.git",
|
|
33
|
+
"directory": "packages/scanner"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"mcp",
|
|
37
|
+
"security",
|
|
38
|
+
"scanner",
|
|
39
|
+
"trust",
|
|
40
|
+
"ai-agent",
|
|
41
|
+
"verification",
|
|
42
|
+
"audit"
|
|
43
|
+
],
|
|
44
|
+
"homepage": "https://github.com/sentinel-atl/project-sentinel#readme",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/sentinel-atl/project-sentinel/issues"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist",
|
|
50
|
+
"README.md"
|
|
51
|
+
]
|
|
52
|
+
}
|