sentinel-verify 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/CHANGELOG.md +84 -0
- package/LICENSE +21 -0
- package/README.md +127 -0
- package/dist/cli/commands/hook.js +133 -0
- package/dist/cli/commands/hook.js.map +1 -0
- package/dist/cli/commands/init.js +21 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/testHallucination.js +35 -0
- package/dist/cli/commands/testHallucination.js.map +1 -0
- package/dist/cli/commands/testSecurity.js +35 -0
- package/dist/cli/commands/testSecurity.js.map +1 -0
- package/dist/cli/commands/testStyle.js +41 -0
- package/dist/cli/commands/testStyle.js.map +1 -0
- package/dist/cli/commands/testVerify.js +36 -0
- package/dist/cli/commands/testVerify.js.map +1 -0
- package/dist/cli/diffInput.js +17 -0
- package/dist/cli/diffInput.js.map +1 -0
- package/dist/cli/index.js +93 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/env.js +16 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/sentinelrc.js +104 -0
- package/dist/config/sentinelrc.js.map +1 -0
- package/dist/dashboard/page.js +270 -0
- package/dist/dashboard/page.js.map +1 -0
- package/dist/dashboard/server.js +65 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/mcp/server.js +141 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/providers/anthropic.js +72 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/index.js +37 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/ollama.js +102 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/types.js +3 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/storage/db.js +123 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/verify/combine.js +42 -0
- package/dist/verify/combine.js.map +1 -0
- package/dist/verify/diff.js +27 -0
- package/dist/verify/diff.js.map +1 -0
- package/dist/verify/evidence.js +25 -0
- package/dist/verify/evidence.js.map +1 -0
- package/dist/verify/hallucination.js +299 -0
- package/dist/verify/hallucination.js.map +1 -0
- package/dist/verify/security.js +274 -0
- package/dist/verify/security.js.map +1 -0
- package/dist/verify/style.js +489 -0
- package/dist/verify/style.js.map +1 -0
- package/dist/verify/taskMatch.js +107 -0
- package/dist/verify/taskMatch.js.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
// Purpose: core logic for check_hallucination — flags references to packages, files, and symbols that don't exist, combining static checks with model judgment.
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
4
|
+
import { builtinModules } from "node:module";
|
|
5
|
+
import { dirname, join, normalize } from "node:path";
|
|
6
|
+
import { z } from "zod/v4";
|
|
7
|
+
import { resolveProvider } from "../providers/index.js";
|
|
8
|
+
import { openDatabase, insertVerification } from "../storage/db.js";
|
|
9
|
+
import { parseDiff } from "./diff.js";
|
|
10
|
+
import { dropDuplicateModelIssues } from "./combine.js";
|
|
11
|
+
import { loadSentinelrc, configPromptBlocks } from "../config/sentinelrc.js";
|
|
12
|
+
import { storeEvidence } from "./evidence.js";
|
|
13
|
+
export const HALLUCINATION_TOOL_NAME = "check_hallucination";
|
|
14
|
+
export const HALLUCINATION_VERDICTS = ["no_issues", "possible_hallucination"];
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Static evidence check (JavaScript/TypeScript files)
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
const JS_FILE = /\.(ts|tsx|js|jsx|mjs|cjs)$/;
|
|
19
|
+
const NODE_BUILTINS = new Set(builtinModules);
|
|
20
|
+
const RESOLVE_SUFFIXES = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", "/index.ts", "/index.js"];
|
|
21
|
+
/** "@scope/pkg/sub/path" -> "@scope/pkg"; "pkg/sub" -> "pkg". */
|
|
22
|
+
function packageNameOf(specifier) {
|
|
23
|
+
const parts = specifier.split("/");
|
|
24
|
+
return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
25
|
+
}
|
|
26
|
+
/** Reads declared dependency names from package.json; null when there is no manifest to check against. */
|
|
27
|
+
function loadDeclaredDeps(projectDir) {
|
|
28
|
+
let pkg;
|
|
29
|
+
try {
|
|
30
|
+
pkg = JSON.parse(readFileSync(join(projectDir, "package.json"), "utf8"));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const deps = new Set();
|
|
36
|
+
for (const key of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
37
|
+
for (const name of Object.keys(pkg[key] ?? {}))
|
|
38
|
+
deps.add(name);
|
|
39
|
+
}
|
|
40
|
+
return deps;
|
|
41
|
+
}
|
|
42
|
+
function extractImports(lineText) {
|
|
43
|
+
const found = [];
|
|
44
|
+
const seen = new Set();
|
|
45
|
+
// Named imports: import { a, b as c } from "spec" (also: import Default, { a } from "spec")
|
|
46
|
+
const namedRe = /import\s+(?:type\s+)?(?:[\w$]+\s*,\s*)?\{([^}]+)\}\s*from\s*["']([^"']+)["']/g;
|
|
47
|
+
for (const m of lineText.matchAll(namedRe)) {
|
|
48
|
+
const names = m[1]
|
|
49
|
+
.split(",")
|
|
50
|
+
.map((part) => part.replace(/\btype\b/, "").trim().split(/\s+as\s+/)[0].trim())
|
|
51
|
+
.filter((n) => n.length > 0);
|
|
52
|
+
found.push({ specifier: m[2], namedImports: names });
|
|
53
|
+
seen.add(m[2]);
|
|
54
|
+
}
|
|
55
|
+
// Every other import shape: bare specifier only.
|
|
56
|
+
const specRe = /(?:import|export)\s[^"';]*?from\s*["']([^"']+)["']|import\s*\(\s*["']([^"']+)["']\s*\)|import\s+["']([^"']+)["']|require\s*\(\s*["']([^"']+)["']\s*\)/g;
|
|
57
|
+
for (const m of lineText.matchAll(specRe)) {
|
|
58
|
+
const specifier = m[1] ?? m[2] ?? m[3] ?? m[4];
|
|
59
|
+
if (specifier && !seen.has(specifier)) {
|
|
60
|
+
found.push({ specifier, namedImports: [] });
|
|
61
|
+
seen.add(specifier);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return found;
|
|
65
|
+
}
|
|
66
|
+
/** Resolves a relative import to an existing file on disk, trying TypeScript-style suffixes. */
|
|
67
|
+
function resolveRelativeImport(projectDir, fromFile, specifier) {
|
|
68
|
+
const base = normalize(join(dirname(fromFile), specifier));
|
|
69
|
+
const candidates = [base];
|
|
70
|
+
// NodeNext style: "./foo.js" in source usually means "./foo.ts" on disk.
|
|
71
|
+
if (/\.(js|mjs|cjs)$/.test(base)) {
|
|
72
|
+
candidates.push(base.replace(/\.(js|mjs|cjs)$/, ".ts"), base.replace(/\.(js|mjs|cjs)$/, ".tsx"));
|
|
73
|
+
}
|
|
74
|
+
for (const candidate of candidates) {
|
|
75
|
+
for (const suffix of RESOLVE_SUFFIXES) {
|
|
76
|
+
if (existsSync(join(projectDir, candidate + suffix)))
|
|
77
|
+
return candidate + suffix;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/** Checks whether a file exports a given name (basic textual check, not type-checking). */
|
|
83
|
+
function fileExportsSymbol(filePath, name) {
|
|
84
|
+
let source;
|
|
85
|
+
try {
|
|
86
|
+
source = readFileSync(filePath, "utf8");
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return true; // unreadable — don't guess, don't flag
|
|
90
|
+
}
|
|
91
|
+
// Barrel re-exports make textual verification unreliable — assume the symbol is fine.
|
|
92
|
+
if (/export\s+\*\s+from/.test(source))
|
|
93
|
+
return true;
|
|
94
|
+
const esc = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
95
|
+
const declaration = new RegExp(`export\\s+(?:declare\\s+)?(?:default\\s+)?(?:abstract\\s+)?(?:async\\s+)?` +
|
|
96
|
+
`(?:function\\*?|const|let|var|class|type|interface|enum)\\s+${esc}\\b`);
|
|
97
|
+
const exportList = new RegExp(`export\\s*(?:type\\s*)?\\{[^}]*\\b${esc}\\b[^}]*\\}`);
|
|
98
|
+
return declaration.test(source) || exportList.test(source);
|
|
99
|
+
}
|
|
100
|
+
/** Dependency names added inside package.json hunks of this same diff (change may not be applied yet). */
|
|
101
|
+
function depsAddedInDiff(added) {
|
|
102
|
+
const names = new Set();
|
|
103
|
+
for (const line of added) {
|
|
104
|
+
if (!line.file.endsWith("package.json"))
|
|
105
|
+
continue;
|
|
106
|
+
const m = /^\s*"((?:@[\w.-]+\/)?[\w.-]+)":\s*"/.exec(line.text);
|
|
107
|
+
if (m)
|
|
108
|
+
names.add(m[1]);
|
|
109
|
+
}
|
|
110
|
+
return names;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The local evidence check: verifies that imported packages are declared,
|
|
114
|
+
* imported files exist, and imported symbols are actually exported.
|
|
115
|
+
*/
|
|
116
|
+
export function staticHallucinationCheck(projectDir, diff) {
|
|
117
|
+
const { added, touchedFiles } = parseDiff(diff);
|
|
118
|
+
const declaredDeps = loadDeclaredDeps(projectDir);
|
|
119
|
+
const diffDeps = depsAddedInDiff(added);
|
|
120
|
+
const issues = [];
|
|
121
|
+
for (const line of added) {
|
|
122
|
+
if (!JS_FILE.test(line.file))
|
|
123
|
+
continue;
|
|
124
|
+
for (const { specifier, namedImports } of extractImports(line.text)) {
|
|
125
|
+
const location = `${line.file}:${line.lineNo} — ${line.text.trim()}`;
|
|
126
|
+
if (specifier.startsWith("node:") || NODE_BUILTINS.has(packageNameOf(specifier))) {
|
|
127
|
+
continue; // Node built-in
|
|
128
|
+
}
|
|
129
|
+
if (specifier.startsWith(".")) {
|
|
130
|
+
// Relative import: the file must exist (or be created in this same diff).
|
|
131
|
+
const resolved = resolveRelativeImport(projectDir, line.file, specifier);
|
|
132
|
+
if (resolved === null) {
|
|
133
|
+
const inDiff = [...touchedFiles].some((f) => normalize(f).startsWith(normalize(join(dirname(line.file), specifier))));
|
|
134
|
+
if (!inDiff) {
|
|
135
|
+
issues.push({
|
|
136
|
+
location,
|
|
137
|
+
reason: `Imported file "${specifier}" does not exist in the project.`,
|
|
138
|
+
source: "static",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
// File exists: verify each named import is actually exported.
|
|
144
|
+
for (const name of namedImports) {
|
|
145
|
+
if (!fileExportsSymbol(join(projectDir, resolved), name)) {
|
|
146
|
+
issues.push({
|
|
147
|
+
location,
|
|
148
|
+
reason: `"${name}" is imported from "${specifier}", but ${resolved} does not export it.`,
|
|
149
|
+
source: "static",
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// Package import: must be declared in package.json, installed, or added in this diff.
|
|
156
|
+
if (declaredDeps === null)
|
|
157
|
+
continue; // no manifest — nothing to prove against
|
|
158
|
+
const pkgName = packageNameOf(specifier);
|
|
159
|
+
const declared = declaredDeps.has(pkgName) ||
|
|
160
|
+
diffDeps.has(pkgName) ||
|
|
161
|
+
existsSync(join(projectDir, "node_modules", pkgName));
|
|
162
|
+
if (!declared) {
|
|
163
|
+
issues.push({
|
|
164
|
+
location,
|
|
165
|
+
reason: `Package "${pkgName}" is imported but is not in package.json, node_modules, or this diff.`,
|
|
166
|
+
source: "static",
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return issues;
|
|
173
|
+
}
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// Model judgment
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
const modelIssuesSchema = z.object({
|
|
178
|
+
issues: z.array(z.object({
|
|
179
|
+
location: z.string().describe("File and line, or the offending code snippet from the diff."),
|
|
180
|
+
reason: z.string().describe("Why this looks hallucinated, in one or two sentences."),
|
|
181
|
+
})),
|
|
182
|
+
});
|
|
183
|
+
// Deliberately short and neutral: long, example-heavy prompts prime small
|
|
184
|
+
// local models to invent findings and loop under schema constraints.
|
|
185
|
+
const SYSTEM_PROMPT = `You review a code diff for hallucinated references to EXTERNAL code: imports of packages that do not exist, or calls to library methods/APIs that the real library does not have.
|
|
186
|
+
|
|
187
|
+
Never flag:
|
|
188
|
+
- Functions, classes, or variables that the diff itself defines — that is the developer's own code.
|
|
189
|
+
- Node.js built-in modules (fs, path, crypto, ...) and their real APIs.
|
|
190
|
+
- Packages listed in the project dependencies, used according to their real API.
|
|
191
|
+
- Symbols that may be defined elsewhere in this project.
|
|
192
|
+
- Style or security concerns.
|
|
193
|
+
|
|
194
|
+
Most diffs are clean; an empty issues list is a normal answer. Only report an issue when you are certain the package or API genuinely does not exist.`;
|
|
195
|
+
function buildPrompt(diff, declaredDeps) {
|
|
196
|
+
// Note: overlap with the static findings is fine — duplicates are removed
|
|
197
|
+
// deterministically afterwards. Keeping this prompt short matters: long,
|
|
198
|
+
// rule-heavy prompts make small local models loop under schema constraints.
|
|
199
|
+
const depsText = declaredDeps
|
|
200
|
+
? [...declaredDeps].sort().join(", ") || "(none declared)"
|
|
201
|
+
: "(no package.json found)";
|
|
202
|
+
return `<project_dependencies>
|
|
203
|
+
${depsText}
|
|
204
|
+
</project_dependencies>
|
|
205
|
+
|
|
206
|
+
<code_diff>
|
|
207
|
+
${diff}
|
|
208
|
+
</code_diff>
|
|
209
|
+
|
|
210
|
+
List any hallucinated APIs, methods, packages, or usage patterns in this diff (empty list if none).`;
|
|
211
|
+
}
|
|
212
|
+
/** Names of functions/classes/etc. that the diff itself defines — never hallucinations. */
|
|
213
|
+
function symbolsDefinedInDiff(added) {
|
|
214
|
+
const names = new Set();
|
|
215
|
+
const declRe = /(?:function\*?|class|const|let|var|interface|type|enum)\s+([A-Za-z_$][\w$]*)/g;
|
|
216
|
+
for (const line of added) {
|
|
217
|
+
for (const m of line.text.matchAll(declRe))
|
|
218
|
+
names.add(m[1]);
|
|
219
|
+
}
|
|
220
|
+
return names;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Agent-native (MCP) mode: runs only the deterministic filesystem checks —
|
|
224
|
+
* no model call — and returns the evidence for the calling agent to judge.
|
|
225
|
+
*/
|
|
226
|
+
export function gatherHallucinationEvidence(projectDir, diff) {
|
|
227
|
+
if (diff.trim().length === 0) {
|
|
228
|
+
throw new Error("Diff is empty — there is no code change to check.");
|
|
229
|
+
}
|
|
230
|
+
const config = loadSentinelrc(projectDir);
|
|
231
|
+
const staticFindings = staticHallucinationCheck(projectDir, diff);
|
|
232
|
+
const deps = loadDeclaredDeps(projectDir);
|
|
233
|
+
storeEvidence(projectDir, HALLUCINATION_TOOL_NAME, diff, staticFindings);
|
|
234
|
+
return {
|
|
235
|
+
mode: "evidence",
|
|
236
|
+
instruction: "The staticFindings below are verified against the actual filesystem — treat them as facts, not opinions. " +
|
|
237
|
+
"Additionally judge the diff yourself for hallucinations the filesystem cannot prove: " +
|
|
238
|
+
"made-up methods on real libraries, invented API signatures or parameters, or usage that does not match " +
|
|
239
|
+
"the real library's API (declaredDependencies lists what the project actually uses). " +
|
|
240
|
+
"Combine both into your conclusion. Honor projectRules if present.",
|
|
241
|
+
projectContext: config?.context ?? "",
|
|
242
|
+
projectRules: config?.rules ?? [],
|
|
243
|
+
staticFindings,
|
|
244
|
+
declaredDependencies: deps ? [...deps].sort() : null,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Standalone (CLI) mode: static evidence first, then model judgment,
|
|
249
|
+
* combined into one verdict. The result is stored locally.
|
|
250
|
+
*/
|
|
251
|
+
export async function checkHallucination(projectDir, diff) {
|
|
252
|
+
if (diff.trim().length === 0) {
|
|
253
|
+
throw new Error("Diff is empty — there is no code change to check.");
|
|
254
|
+
}
|
|
255
|
+
const staticIssues = staticHallucinationCheck(projectDir, diff);
|
|
256
|
+
const config = loadSentinelrc(projectDir);
|
|
257
|
+
const provider = resolveProvider();
|
|
258
|
+
const modelAnswer = await provider.completeStructured({
|
|
259
|
+
system: SYSTEM_PROMPT,
|
|
260
|
+
prompt: configPromptBlocks(config) + buildPrompt(diff, loadDeclaredDeps(projectDir)),
|
|
261
|
+
schema: modelIssuesSchema,
|
|
262
|
+
schemaName: "hallucination_issues",
|
|
263
|
+
});
|
|
264
|
+
// The model often re-reports what the static check already proved — remove
|
|
265
|
+
// duplicates deterministically. Tiny local models also reliably mistake
|
|
266
|
+
// symbols the diff DEFINES for hallucinated references, despite prompt
|
|
267
|
+
// instructions — filter those out too (matching only quoted mentions, so
|
|
268
|
+
// common words in prose don't trigger the filter).
|
|
269
|
+
const definedSymbols = symbolsDefinedInDiff(parseDiff(diff).added);
|
|
270
|
+
const modelIssues = dropDuplicateModelIssues(staticIssues, modelAnswer.issues.map((issue) => ({ ...issue, source: "model" }))).filter((issue) => {
|
|
271
|
+
const text = `${issue.location} ${issue.reason}`;
|
|
272
|
+
return ![...definedSymbols].some((name) => {
|
|
273
|
+
const esc = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
274
|
+
return new RegExp(`[\`'"]${esc}[\`'"]`).test(text);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
const issues = [...staticIssues, ...modelIssues];
|
|
278
|
+
const verdict = issues.length > 0 ? "possible_hallucination" : "no_issues";
|
|
279
|
+
const db = openDatabase(projectDir);
|
|
280
|
+
try {
|
|
281
|
+
insertVerification(db, {
|
|
282
|
+
tool: HALLUCINATION_TOOL_NAME,
|
|
283
|
+
task: "",
|
|
284
|
+
diffHash: createHash("sha256").update(diff).digest("hex"),
|
|
285
|
+
verdict,
|
|
286
|
+
reason: issues.length === 0
|
|
287
|
+
? "No hallucination indicators found."
|
|
288
|
+
: `${issues.length} possible hallucination(s) found.`,
|
|
289
|
+
details: JSON.stringify(issues),
|
|
290
|
+
provider: provider.name,
|
|
291
|
+
model: provider.model,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
finally {
|
|
295
|
+
db.close();
|
|
296
|
+
}
|
|
297
|
+
return { verdict, issues, provider: provider.name, model: provider.model };
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=hallucination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hallucination.js","sourceRoot":"","sources":["../../src/verify/hallucination.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// Purpose: core logic for check_security — flags leaked secrets and unsafe patterns with deterministic rules, plus advisory model judgment for context-dependent risks.
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { z } from "zod/v4";
|
|
4
|
+
import { resolveProvider } from "../providers/index.js";
|
|
5
|
+
import { openDatabase, insertVerification } from "../storage/db.js";
|
|
6
|
+
import { parseDiff } from "./diff.js";
|
|
7
|
+
import { dropDuplicateModelIssues } from "./combine.js";
|
|
8
|
+
import { loadSentinelrc, configPromptBlocks } from "../config/sentinelrc.js";
|
|
9
|
+
import { storeEvidence } from "./evidence.js";
|
|
10
|
+
export const SECURITY_TOOL_NAME = "check_security";
|
|
11
|
+
export const SECURITY_VERDICTS = ["no_issues", "security_risk"];
|
|
12
|
+
// Note for maintainers: reasons quote a stable identifying token in double
|
|
13
|
+
// quotes — the combiner uses those quoted tokens to drop duplicate model
|
|
14
|
+
// findings, so keep the quotes when editing wording.
|
|
15
|
+
const SECRET_RULES = [
|
|
16
|
+
{
|
|
17
|
+
pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/,
|
|
18
|
+
reason: 'Possible AWS access key ID (starts with "AKIA"/"ASIA"): {match}',
|
|
19
|
+
mask: true,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
pattern: /\bgh[pousr]_[A-Za-z0-9]{36,}\b/,
|
|
23
|
+
reason: 'Possible GitHub token (starts with "gh"): {match}',
|
|
24
|
+
mask: true,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
pattern: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/,
|
|
28
|
+
reason: 'Possible Slack token (starts with "xox"): {match}',
|
|
29
|
+
mask: true,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
pattern: /\bAIza[0-9A-Za-z_-]{35}\b/,
|
|
33
|
+
reason: 'Possible Google API key (starts with "AIza"): {match}',
|
|
34
|
+
mask: true,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
pattern: /\b[sr]k_live_[A-Za-z0-9]{24,}\b/,
|
|
38
|
+
reason: 'Possible live Stripe secret key ("k_live_"): {match}',
|
|
39
|
+
mask: true,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
pattern: /\bsk-ant-[A-Za-z0-9_-]{20,}\b/,
|
|
43
|
+
reason: 'Possible Anthropic API key (starts with "sk-ant-"): {match}',
|
|
44
|
+
mask: true,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
pattern: /\bsk-(?:proj-)?[A-Za-z0-9_-]{32,}\b/,
|
|
48
|
+
reason: 'Possible OpenAI API key (starts with "sk-"): {match}',
|
|
49
|
+
mask: true,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
pattern: /\bnpm_[A-Za-z0-9]{36}\b/,
|
|
53
|
+
reason: 'Possible npm token (starts with "npm_"): {match}',
|
|
54
|
+
mask: true,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----/,
|
|
58
|
+
reason: 'A "PRIVATE KEY" block is being committed.',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
pattern: /\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\./,
|
|
62
|
+
reason: 'Possible JWT (starts with "eyJ"): {match}',
|
|
63
|
+
mask: true,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
pattern: /\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp):\/\/[^\s:@/"']+:([^\s@/"']+)@/,
|
|
67
|
+
reason: 'Connection string ("://") with an embedded password: {match}',
|
|
68
|
+
mask: true,
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
const UNSAFE_RULES = [
|
|
72
|
+
{
|
|
73
|
+
pattern: /\beval\s*\(/,
|
|
74
|
+
reason: 'Call to "eval" — executes arbitrary code from a string.',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
pattern: /\bnew\s+Function\s*\(/,
|
|
78
|
+
reason: 'Use of "new Function" — compiles arbitrary code from a string, like eval.',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
pattern: /\bexec(?:Sync)?\s*\(\s*(?:`[^`]*\$\{|["'][^"']*["']\s*\+|[\w$.]+\s*\+)/,
|
|
82
|
+
reason: 'Shell command built by string concatenation passed to "exec" — command injection risk. Use execFile with an argument array instead.',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
pattern: /\bshell\s*:\s*true\b/,
|
|
86
|
+
reason: 'Child process spawned with "shell: true" — command injection risk if any input reaches the command line.',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
pattern: /os\.system\s*\(\s*(?:.*\+|f["'])/,
|
|
90
|
+
reason: 'Shell command built from dynamic input passed to "os.system" — command injection risk.',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
pattern: /subprocess\.\w+\s*\(.*shell\s*=\s*True/,
|
|
94
|
+
reason: 'Subprocess called with "shell=True" — command injection risk if any input reaches the command line.',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
pattern: /rejectUnauthorized\s*:\s*false/,
|
|
98
|
+
reason: 'TLS certificate verification disabled ("rejectUnauthorized: false").',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
pattern: /NODE_TLS_REJECT_UNAUTHORIZED["']?\s*[:=]\s*["']?0/,
|
|
102
|
+
reason: 'TLS certificate verification disabled process-wide ("NODE_TLS_REJECT_UNAUTHORIZED=0").',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
pattern: /\bverify\s*=\s*False\b/,
|
|
106
|
+
reason: 'TLS certificate verification disabled ("verify=False").',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
pattern: /InsecureSkipVerify\s*:\s*true/,
|
|
110
|
+
reason: 'TLS certificate verification disabled ("InsecureSkipVerify: true").',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
pattern: /\bcurl\b[^|;&]*(?:\s-k\b|--insecure\b)/,
|
|
114
|
+
reason: 'curl called with certificate verification disabled ("--insecure"/"-k").',
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
// Generic hardcoded-credential assignment, gated by an entropy check so
|
|
118
|
+
// placeholders and dictionary words don't fire.
|
|
119
|
+
const CREDENTIAL_ASSIGNMENT = /(password|passwd|pwd|secret|token|api[_-]?key|apikey|access[_-]?key|private[_-]?key|auth)["']?\s*[:=]\s*["']([^"']{8,})["']/i;
|
|
120
|
+
const PLACEHOLDER_VALUE = /^(?:\$\{|<|%|\*|x{3,}|your|my|the|change|example|dummy|test|placeholder|password|secret|redacted|todo|fixme|sample|insert|replace|none|null|undefined|abc|123)/i;
|
|
121
|
+
/** Shannon entropy in bits per character — high values look like real key material. */
|
|
122
|
+
function shannonEntropy(value) {
|
|
123
|
+
const freq = new Map();
|
|
124
|
+
for (const ch of value)
|
|
125
|
+
freq.set(ch, (freq.get(ch) ?? 0) + 1);
|
|
126
|
+
let entropy = 0;
|
|
127
|
+
for (const count of freq.values()) {
|
|
128
|
+
const p = count / value.length;
|
|
129
|
+
entropy -= p * Math.log2(p);
|
|
130
|
+
}
|
|
131
|
+
return entropy;
|
|
132
|
+
}
|
|
133
|
+
/** "AKIAIOSFODNN7EXAMPLE" -> "AKIA…" — never echo a whole secret anywhere. */
|
|
134
|
+
function maskSecret(value) {
|
|
135
|
+
return value.length <= 8 ? "…" : `${value.slice(0, 4)}…`;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The deterministic security check: leaked secrets and known unsafe patterns
|
|
139
|
+
* in the added lines of a diff. Matched secrets are masked in the output.
|
|
140
|
+
*/
|
|
141
|
+
export function staticSecurityCheck(diff) {
|
|
142
|
+
const { added } = parseDiff(diff);
|
|
143
|
+
const issues = [];
|
|
144
|
+
for (const line of added) {
|
|
145
|
+
const matchedSpans = [];
|
|
146
|
+
const overlaps = (start, end) => matchedSpans.some(([s, e]) => start < e && end > s);
|
|
147
|
+
const applyRule = (rule) => {
|
|
148
|
+
const match = rule.pattern.exec(line.text);
|
|
149
|
+
if (!match)
|
|
150
|
+
return;
|
|
151
|
+
const start = match.index;
|
|
152
|
+
const end = start + match[0].length;
|
|
153
|
+
if (overlaps(start, end))
|
|
154
|
+
return; // a more specific rule already claimed this span
|
|
155
|
+
matchedSpans.push([start, end]);
|
|
156
|
+
const shown = rule.mask ? maskSecret(match[1] ?? match[0]) : match[0];
|
|
157
|
+
const lineText = rule.mask
|
|
158
|
+
? line.text.replace(match[1] ?? match[0], maskSecret(match[1] ?? match[0]))
|
|
159
|
+
: line.text;
|
|
160
|
+
issues.push({
|
|
161
|
+
location: `${line.file}:${line.lineNo} — ${lineText.trim()}`,
|
|
162
|
+
reason: rule.reason.replace("{match}", shown),
|
|
163
|
+
source: "static",
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
for (const rule of SECRET_RULES)
|
|
167
|
+
applyRule(rule);
|
|
168
|
+
for (const rule of UNSAFE_RULES)
|
|
169
|
+
applyRule(rule);
|
|
170
|
+
// Generic credential assignment (skipped if a specific rule already fired on this span).
|
|
171
|
+
const cred = CREDENTIAL_ASSIGNMENT.exec(line.text);
|
|
172
|
+
if (cred) {
|
|
173
|
+
const [, keyName, value] = cred;
|
|
174
|
+
const valueStart = line.text.indexOf(value, cred.index);
|
|
175
|
+
const looksReal = !PLACEHOLDER_VALUE.test(value) &&
|
|
176
|
+
!/\$\{|process\.env|os\.environ|getenv/.test(value) &&
|
|
177
|
+
(shannonEntropy(value) >= 3.5 || (value.length >= 16 && shannonEntropy(value) >= 3.0));
|
|
178
|
+
if (looksReal && !overlaps(valueStart, valueStart + value.length)) {
|
|
179
|
+
issues.push({
|
|
180
|
+
location: `${line.file}:${line.lineNo} — ${line.text.replace(value, maskSecret(value)).trim()}`,
|
|
181
|
+
reason: `Hardcoded credential: "${keyName}" is assigned what looks like a real secret (${maskSecret(value)}). Read it from the environment instead.`,
|
|
182
|
+
source: "static",
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return issues;
|
|
188
|
+
}
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
// Model judgment (advisory)
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
const modelIssuesSchema = z.object({
|
|
193
|
+
issues: z.array(z.object({
|
|
194
|
+
location: z.string().describe("File and line, or the offending code snippet from the diff."),
|
|
195
|
+
reason: z.string().describe("Why this is a security risk, in one or two sentences."),
|
|
196
|
+
})),
|
|
197
|
+
});
|
|
198
|
+
// Deliberately short and neutral — long, example-heavy prompts make small
|
|
199
|
+
// local models fabricate findings and loop under schema constraints.
|
|
200
|
+
const SYSTEM_PROMPT = `You review a code diff for security problems: hardcoded or leaked credentials, injection risks (SQL, shell, path), disabled security checks, or authentication and permission logic that is clearly wrong.
|
|
201
|
+
|
|
202
|
+
Most diffs are clean; an empty issues list is a normal answer. Only report issues you are confident about; do not guess. Do not flag style, performance, or hypothetical concerns.`;
|
|
203
|
+
function buildPrompt(diff) {
|
|
204
|
+
return `<code_diff>
|
|
205
|
+
${diff}
|
|
206
|
+
</code_diff>
|
|
207
|
+
|
|
208
|
+
List security problems in this diff (empty list if none).`;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Agent-native (MCP) mode: runs only the deterministic secret and
|
|
212
|
+
* unsafe-pattern rules — no model call — and returns the evidence for the
|
|
213
|
+
* calling agent to judge.
|
|
214
|
+
*/
|
|
215
|
+
export function gatherSecurityEvidence(projectDir, diff) {
|
|
216
|
+
if (diff.trim().length === 0) {
|
|
217
|
+
throw new Error("Diff is empty — there is no code change to check.");
|
|
218
|
+
}
|
|
219
|
+
const config = loadSentinelrc(projectDir);
|
|
220
|
+
const staticFindings = staticSecurityCheck(diff);
|
|
221
|
+
storeEvidence(projectDir, SECURITY_TOOL_NAME, diff, staticFindings);
|
|
222
|
+
return {
|
|
223
|
+
mode: "evidence",
|
|
224
|
+
instruction: "The staticFindings below are deterministic pattern detections (any matched secrets are masked and were " +
|
|
225
|
+
"never stored in full). Additionally judge the diff yourself for context-dependent risks the patterns " +
|
|
226
|
+
"cannot catch: injection paths, authentication or permission logic that looks wrong, unsafe handling of " +
|
|
227
|
+
"user input. Combine both into your conclusion. Honor projectRules if present.",
|
|
228
|
+
projectContext: config?.context ?? "",
|
|
229
|
+
projectRules: config?.rules ?? [],
|
|
230
|
+
staticFindings,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Standalone (CLI) mode: deterministic rules first (authoritative), then
|
|
235
|
+
* model judgment (advisory, deduplicated). The result is stored locally
|
|
236
|
+
* with secrets masked.
|
|
237
|
+
*/
|
|
238
|
+
export async function checkSecurity(projectDir, diff) {
|
|
239
|
+
if (diff.trim().length === 0) {
|
|
240
|
+
throw new Error("Diff is empty — there is no code change to check.");
|
|
241
|
+
}
|
|
242
|
+
const staticIssues = staticSecurityCheck(diff);
|
|
243
|
+
const config = loadSentinelrc(projectDir);
|
|
244
|
+
const provider = resolveProvider();
|
|
245
|
+
const modelAnswer = await provider.completeStructured({
|
|
246
|
+
system: SYSTEM_PROMPT,
|
|
247
|
+
prompt: configPromptBlocks(config) + buildPrompt(diff),
|
|
248
|
+
schema: modelIssuesSchema,
|
|
249
|
+
schemaName: "security_issues",
|
|
250
|
+
});
|
|
251
|
+
const modelIssues = dropDuplicateModelIssues(staticIssues, modelAnswer.issues.map((issue) => ({ ...issue, source: "model" })));
|
|
252
|
+
const issues = [...staticIssues, ...modelIssues];
|
|
253
|
+
const verdict = issues.length > 0 ? "security_risk" : "no_issues";
|
|
254
|
+
const db = openDatabase(projectDir);
|
|
255
|
+
try {
|
|
256
|
+
insertVerification(db, {
|
|
257
|
+
tool: SECURITY_TOOL_NAME,
|
|
258
|
+
task: "",
|
|
259
|
+
diffHash: createHash("sha256").update(diff).digest("hex"),
|
|
260
|
+
verdict,
|
|
261
|
+
reason: issues.length === 0
|
|
262
|
+
? "No security risks found."
|
|
263
|
+
: `${issues.length} security risk(s) found.`,
|
|
264
|
+
details: JSON.stringify(issues),
|
|
265
|
+
provider: provider.name,
|
|
266
|
+
model: provider.model,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
finally {
|
|
270
|
+
db.close();
|
|
271
|
+
}
|
|
272
|
+
return { verdict, issues, provider: provider.name, model: provider.model };
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/verify/security.ts"],"names":[],"mappings":"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"}
|