@securityreviewai/securityreview-kit 0.1.48 → 0.1.50
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/api.js +44 -0
- package/dist/commands/guardrails.js +13 -0
- package/dist/commands/init.js +88 -0
- package/dist/commands/profile.js +14 -0
- package/dist/commands/status.js +27 -0
- package/dist/commands/sync.js +6 -0
- package/dist/config.js +18 -0
- package/dist/fs.js +43 -0
- package/dist/index.js +44 -0
- package/dist/profile.js +113 -0
- package/dist/scaffold/claude-code.js +43 -0
- package/dist/scaffold/codex.js +41 -0
- package/dist/scaffold/cursor.js +45 -0
- package/dist/scaffold/gemini.js +10 -0
- package/dist/scaffold/index.js +22 -0
- package/dist/scaffold/mcp.js +15 -0
- package/dist/scaffold/rules.js +191 -0
- package/dist/scaffold/vibreview.js +30 -0
- package/dist/scaffold/vscode.js +28 -0
- package/dist/scaffold/windsurf.js +10 -0
- package/dist/sync/index.js +34 -0
- package/dist/sync/payload.js +23 -0
- package/dist/sync/state.js +12 -0
- package/dist/types.js +1 -0
- package/package.json +24 -30
- package/templates/claude/CLAUDE.md +13 -0
- package/templates/claude/agents/guardrail_profiler.md +12 -0
- package/templates/claude/agents/threat_modeler.md +5 -0
- package/templates/claude/skills/vibreview/SKILL.md +21 -0
- package/templates/claude/skills/vibreview/guardrail_patterns.md +12 -0
- package/templates/cursor/rules/vibreview-security.mdc +8 -0
- package/README.md +0 -105
- package/bin/securityreview-kit.js +0 -5
- package/src/cli.js +0 -109
- package/src/commands/init.js +0 -851
- package/src/commands/status.js +0 -99
- package/src/commands/switch-project.js +0 -207
- package/src/generators/mcp/claude.js +0 -85
- package/src/generators/mcp/claude.test.js +0 -64
- package/src/generators/mcp/codex.js +0 -70
- package/src/generators/mcp/codex.test.js +0 -43
- package/src/generators/mcp/cursor.js +0 -29
- package/src/generators/mcp/cursor.test.js +0 -50
- package/src/generators/mcp/gemini.js +0 -28
- package/src/generators/mcp/vscode.js +0 -29
- package/src/generators/mcp/windsurf.js +0 -27
- package/src/generators/rules/antigravity.js +0 -22
- package/src/generators/rules/claude.js +0 -87
- package/src/generators/rules/claude.test.js +0 -60
- package/src/generators/rules/codex.js +0 -141
- package/src/generators/rules/codex.test.js +0 -59
- package/src/generators/rules/content.js +0 -110
- package/src/generators/rules/cursor.js +0 -128
- package/src/generators/rules/gemini.js +0 -13
- package/src/generators/rules/guardrails-init-profile.md +0 -56
- package/src/generators/rules/guardrails-profiler/SKILL.md +0 -130
- package/src/generators/rules/guardrails-profiler/references/signal-registry.json +0 -514
- package/src/generators/rules/guardrails-selection/references/category-threat-map.md +0 -232
- package/src/generators/rules/guardrails_rule.md +0 -94
- package/src/generators/rules/hooks.json +0 -11
- package/src/generators/rules/srai-profile.md +0 -32
- package/src/generators/rules/vscode.js +0 -101
- package/src/generators/rules/vscode.test.js +0 -54
- package/src/generators/rules/windsurf.js +0 -13
- package/src/utils/constants.js +0 -95
- package/src/utils/cursor-agent-path.js +0 -67
- package/src/utils/cursor-cli-permissions.js +0 -28
- package/src/utils/detect.js +0 -27
- package/src/utils/fs-helpers.js +0 -82
- package/src/utils/guardrails-profiler-bundle.js +0 -84
- package/src/utils/ide-cli-install.js +0 -138
- package/src/utils/profiler-agent.js +0 -446
- package/src/utils/profiler-agent.test.js +0 -81
- package/src/utils/srai.js +0 -252
- /package/{src/generators/rules → templates/shared}/content.md +0 -0
- /package/{src/generators/rules/guardrails-selection/SKILL.md → templates/shared/guardrails-selection.md} +0 -0
- /package/{src/generators/rules/skill.md → templates/shared/threat-modelling.md} +0 -0
- /package/{src/generators/rules → templates/shared}/vibereview-sync/SKILL.md +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
const TOOL_PROJECT = "`vibreview_get_tenant_project`";
|
|
3
|
+
const TOOL_GUARDRAILS = "`vibreview_get_guardrails`";
|
|
4
|
+
const TOOL_SYNC = "`vibreview_ctm_sync_markdown`";
|
|
5
|
+
export const SENTINEL_START = "<!-- securityreview-kit:start -->";
|
|
6
|
+
export const SENTINEL_END = "<!-- securityreview-kit:end -->";
|
|
7
|
+
export function ruleContent(config, paths) {
|
|
8
|
+
return renderTemplate("content.md", config, paths);
|
|
9
|
+
}
|
|
10
|
+
export function claudeRuleContent(config) {
|
|
11
|
+
return ruleContent(config, {
|
|
12
|
+
guardrailsSelectionSkillDir: ".claude/skills/guardrails-selection",
|
|
13
|
+
threatModellingSkillDir: ".claude/skills/threat-modelling",
|
|
14
|
+
vibereviewSyncSkillDir: ".claude/skills/vibereview-sync",
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export function copilotRuleContent(config) {
|
|
18
|
+
return ruleContent(config, {
|
|
19
|
+
guardrailsSelectionSkillDir: ".github/skills/guardrails-selection",
|
|
20
|
+
threatModellingSkillDir: ".github/skills/threat-modelling",
|
|
21
|
+
vibereviewSyncSkillDir: ".github/skills/vibereview-sync",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export function codexRuleContent(config) {
|
|
25
|
+
return ruleContent(config, {
|
|
26
|
+
guardrailsSelectionSkillDir: ".codex/skills/guardrails-selection",
|
|
27
|
+
threatModellingSkillDir: ".codex/skills/threat-modelling",
|
|
28
|
+
vibereviewSyncSkillDir: ".codex/skills/vibereview-sync",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function cursorRuleContent(config) {
|
|
32
|
+
return ruleContent(config, {
|
|
33
|
+
guardrailsSelectionSkillDir: ".cursor/skills/guardrails-selection",
|
|
34
|
+
threatModellingSkillDir: ".cursor/skills/threat-modelling",
|
|
35
|
+
vibereviewSyncSkillDir: ".cursor/skills/vibereview-sync",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export function guardrailsSelectionSkillContent(config, paths = defaultPaths("cursor")) {
|
|
39
|
+
return renderTemplate("guardrails-selection.md", config, paths);
|
|
40
|
+
}
|
|
41
|
+
export function threatModelSkillContent(config, paths = defaultPaths("cursor")) {
|
|
42
|
+
return renderTemplate("threat-modelling.md", config, paths);
|
|
43
|
+
}
|
|
44
|
+
export function syncSkillContent(config, scanDir = ".vibreview/scans") {
|
|
45
|
+
return renderTemplate("vibereview-sync/SKILL.md", config, defaultPaths("cursor")).replaceAll(".vibreview/scans/", `${scanDir.replace(/\/$/, "")}/`);
|
|
46
|
+
}
|
|
47
|
+
export function cursorHooksContent(config) {
|
|
48
|
+
return JSON.stringify({
|
|
49
|
+
version: 1,
|
|
50
|
+
hooks: {
|
|
51
|
+
sessionStart: [
|
|
52
|
+
{
|
|
53
|
+
command: `printf '%s\\n' '${jsonPayload(additionalContext(config)).replaceAll("'", "'\\''")}'`,
|
|
54
|
+
timeout: 5,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
}, null, 2) + "\n";
|
|
59
|
+
}
|
|
60
|
+
export function codexHooksContent(config) {
|
|
61
|
+
return JSON.stringify({
|
|
62
|
+
hooks: {
|
|
63
|
+
SessionStart: [
|
|
64
|
+
{
|
|
65
|
+
matcher: "startup|resume",
|
|
66
|
+
hooks: [
|
|
67
|
+
{
|
|
68
|
+
type: "command",
|
|
69
|
+
command: `printf '%s\\n' '${codexPayload(config, "SessionStart").replaceAll("'", "'\\''")}'`,
|
|
70
|
+
timeout: 5,
|
|
71
|
+
statusMessage: "Loading VibeReview security session policy",
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
UserPromptSubmit: [
|
|
77
|
+
{
|
|
78
|
+
hooks: [
|
|
79
|
+
{
|
|
80
|
+
type: "command",
|
|
81
|
+
command: `printf '%s\\n' '${codexPayload(config, "UserPromptSubmit").replaceAll("'", "'\\''")}'`,
|
|
82
|
+
timeout: 5,
|
|
83
|
+
statusMessage: "Refreshing VibeReview security session policy",
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
}, null, 2) + "\n";
|
|
90
|
+
}
|
|
91
|
+
export function vscodeHooksContent(config) {
|
|
92
|
+
return JSON.stringify({
|
|
93
|
+
hooks: {
|
|
94
|
+
SessionStart: [
|
|
95
|
+
{
|
|
96
|
+
type: "command",
|
|
97
|
+
command: `printf '%s\\n' '${codexPayload(config, "SessionStart").replaceAll("'", "'\\''")}'`,
|
|
98
|
+
timeout: 5,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
}, null, 2) + "\n";
|
|
103
|
+
}
|
|
104
|
+
function additionalContext(config) {
|
|
105
|
+
const project = projectContext(config);
|
|
106
|
+
return [
|
|
107
|
+
"## MANDATORY VIBEREVIEW SECURITY GATE",
|
|
108
|
+
"",
|
|
109
|
+
`Configured project: ${project.name} (${project.slug}, ${project.id})`,
|
|
110
|
+
"",
|
|
111
|
+
"For any request with security impact, follow this order:",
|
|
112
|
+
"",
|
|
113
|
+
`1. Resolve the tenant/project with ${TOOL_PROJECT} using project_id="${project.id}" or project_slug="${project.slug}".`,
|
|
114
|
+
`2. Fetch VibeReview guardrails with ${TOOL_GUARDRAILS}, shortlist relevant guardrails, and preserve that shortlist.`,
|
|
115
|
+
"3. Run PWNISMS threat modelling before implementation.",
|
|
116
|
+
"4. Implement secure code using the threat model and shortlisted guardrails.",
|
|
117
|
+
`5. Write one markdown artifact under .vibreview/scans/ and call ${TOOL_SYNC} immediately after writing it.`,
|
|
118
|
+
"",
|
|
119
|
+
"No deferral. No local profiling. The main agent owns the final markdown sync.",
|
|
120
|
+
].join("\\n");
|
|
121
|
+
}
|
|
122
|
+
function codexPayload(config, hookEventName) {
|
|
123
|
+
return JSON.stringify({
|
|
124
|
+
hookSpecificOutput: {
|
|
125
|
+
hookEventName,
|
|
126
|
+
additionalContext: additionalContext(config),
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function jsonPayload(additionalContext) {
|
|
131
|
+
return JSON.stringify({ additional_context: additionalContext });
|
|
132
|
+
}
|
|
133
|
+
function renderTemplate(templatePath, config, paths) {
|
|
134
|
+
const project = projectContext(config);
|
|
135
|
+
const template = readFileSync(new URL(`../../templates/shared/${templatePath}`, import.meta.url), "utf8").trim();
|
|
136
|
+
return template
|
|
137
|
+
.replaceAll("Configured SRAI project name: `<SRAI_PROJECT_NAME>`", [
|
|
138
|
+
`Configured VibeReview project: \`${project.name}\``,
|
|
139
|
+
`Project slug: \`${project.slug}\``,
|
|
140
|
+
`Project id: \`${project.id}\``,
|
|
141
|
+
].join("\n"))
|
|
142
|
+
.replaceAll("Configured SRAI project name: `<SRAI_PROJECT_NAME>`", `Configured VibeReview project: \`${project.name}\``)
|
|
143
|
+
.replaceAll("name=\"<SRAI_PROJECT_NAME>\"", `project_id="${project.id}", project_slug="${project.slug}", project_name="${project.name}"`)
|
|
144
|
+
.replaceAll("<SRAI_PROJECT_NAME>", project.name)
|
|
145
|
+
.replaceAll("{{SRAI_PROJECT_NAME}}", project.name)
|
|
146
|
+
.replaceAll("SRAI", "VibeReview")
|
|
147
|
+
.replaceAll("srai", "vibreview")
|
|
148
|
+
.replaceAll("security-review-mcp", "vibreview")
|
|
149
|
+
.replaceAll("find_project_by_name", "vibreview_get_tenant_project")
|
|
150
|
+
.replaceAll("list_projects", "vibreview_get_tenant_project")
|
|
151
|
+
.replaceAll("create_project", "vibreview_get_tenant_project")
|
|
152
|
+
.replaceAll("get_project", "vibreview_get_tenant_project")
|
|
153
|
+
.replaceAll("get_guardrails", "vibreview_get_guardrails")
|
|
154
|
+
.replaceAll("get_guardrail_by_id", "the exact returned guardrail entry")
|
|
155
|
+
.replaceAll("sync_ai_ide_markdown", "vibreview_ctm_sync_markdown")
|
|
156
|
+
.replaceAll("update_vibe_profile", "server-side repository profiling")
|
|
157
|
+
.replaceAll("write_default_pack", "server-side guardrail pack selection")
|
|
158
|
+
.replaceAll("`vibereview/`", "`.vibreview/scans/`")
|
|
159
|
+
.replaceAll("vibereview/*.md", ".vibreview/scans/*.md")
|
|
160
|
+
.replaceAll("vibereview/<chat_session_id>-<slugified-title-or-event-name>.md", ".vibreview/scans/<chat_session_id>-<slugified-title-or-event-name>.md")
|
|
161
|
+
.replaceAll("under `vibereview/`", "under `.vibreview/scans/`")
|
|
162
|
+
.replaceAll("in `vibereview/`", "in `.vibreview/scans/`")
|
|
163
|
+
.replaceAll("`{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md`", `\`${paths.guardrailsSelectionSkillDir}/SKILL.md\``)
|
|
164
|
+
.replaceAll("`{{THREAT_MODELLING_SKILL_DIR}}/SKILL.md`", `\`${paths.threatModellingSkillDir}/SKILL.md\``)
|
|
165
|
+
.replaceAll("`{{VIBEREVIEW_SYNC_SKILL_DIR}}/SKILL.md`", `\`${paths.vibereviewSyncSkillDir}/SKILL.md\``)
|
|
166
|
+
.replaceAll("{{GUARDRAILS_SELECTION_SKILL_DIR}}", paths.guardrailsSelectionSkillDir)
|
|
167
|
+
.replaceAll("{{THREAT_MODELLING_SKILL_DIR}}", paths.threatModellingSkillDir)
|
|
168
|
+
.replaceAll("{{VIBEREVIEW_SYNC_SKILL_DIR}}", paths.vibereviewSyncSkillDir)
|
|
169
|
+
.replaceAll("name=\"{{SRAI_PROJECT_NAME}}\"", `project_id="${project.id}"`)
|
|
170
|
+
.replaceAll("name=\"<VibeReview_PROJECT_NAME>\"", `project_id="${project.id}", project_slug="${project.slug}", project_name="${project.name}"`)
|
|
171
|
+
.replaceAll("The old profile walkthrough is no longer part of the agent workflow.", "Local profile walkthroughs are no longer part of the IDE workflow; profiling happens server-side after project creation.")
|
|
172
|
+
.replaceAll("Profiler only | `server-side repository profiling`, `server-side guardrail pack selection` are used by init-time profiling, not normal coding tasks", "Profiler only | Server-side repository profiling and guardrail pack selection happen after project creation, not during normal IDE coding tasks");
|
|
173
|
+
}
|
|
174
|
+
function defaultPaths(ide) {
|
|
175
|
+
const root = ide === "claude" ? ".claude/skills" : ide === "vscode" ? ".github/skills" : ide === "codex" ? ".codex/skills" : ".cursor/skills";
|
|
176
|
+
return {
|
|
177
|
+
guardrailsSelectionSkillDir: `${root}/guardrails-selection`,
|
|
178
|
+
threatModellingSkillDir: `${root}/threat-modelling`,
|
|
179
|
+
vibereviewSyncSkillDir: `${root}/vibereview-sync`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function projectContext(config) {
|
|
183
|
+
return {
|
|
184
|
+
id: sanitize(config.project_id || config.project_slug),
|
|
185
|
+
slug: sanitize(config.project_slug),
|
|
186
|
+
name: sanitize(config.project_name || config.project_slug),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function sanitize(value) {
|
|
190
|
+
return value.replace(/[\r\n`]/g, " ").trim();
|
|
191
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { CONFIG_DIR, SCANS_DIR, SYNC_STATE_PATH, writeConfig } from "../config.js";
|
|
3
|
+
import { ensureDir, readJson, upsertBlock, writeJson } from "../fs.js";
|
|
4
|
+
const GITIGNORE_START = "# BEGIN VibeReview";
|
|
5
|
+
const GITIGNORE_END = "# END VibeReview";
|
|
6
|
+
export async function scaffoldVibeReview(cwd, config) {
|
|
7
|
+
await ensureDir(join(cwd, CONFIG_DIR));
|
|
8
|
+
await ensureDir(join(cwd, SCANS_DIR));
|
|
9
|
+
await writeConfig(cwd, config);
|
|
10
|
+
const existingSyncState = await readJson(join(cwd, SYNC_STATE_PATH));
|
|
11
|
+
if (!existingSyncState)
|
|
12
|
+
await writeJson(join(cwd, SYNC_STATE_PATH), { version: 1, artifacts: {} });
|
|
13
|
+
await upsertBlock(join(cwd, ".gitignore"), GITIGNORE_START, GITIGNORE_END, gitignoreEntries(config).join("\n"));
|
|
14
|
+
}
|
|
15
|
+
function gitignoreEntries(config) {
|
|
16
|
+
const entries = [".vibreview/config.json", ".vibreview/sync-state.json", ".vibreview/scans/"];
|
|
17
|
+
if (config.ide.includes("claude_code"))
|
|
18
|
+
entries.push(".mcp.json");
|
|
19
|
+
if (config.ide.includes("cursor"))
|
|
20
|
+
entries.push(".cursor/mcp.json");
|
|
21
|
+
if (config.ide.includes("vscode_copilot"))
|
|
22
|
+
entries.push(".vscode/mcp.json");
|
|
23
|
+
if (config.ide.includes("codex"))
|
|
24
|
+
entries.push(".codex/config.toml");
|
|
25
|
+
if (config.ide.includes("gemini") || config.ide.includes("antigravity"))
|
|
26
|
+
entries.push(".gemini/settings.json");
|
|
27
|
+
if (config.ide.includes("windsurf"))
|
|
28
|
+
entries.push(".windsurf/mcp_config.json");
|
|
29
|
+
return entries;
|
|
30
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { readJson, writeJson, writeText, upsertBlock } from "../fs.js";
|
|
3
|
+
import { mcpRemoteServerWithType } from "./mcp.js";
|
|
4
|
+
import { SENTINEL_END, SENTINEL_START, copilotRuleContent, guardrailsSelectionSkillContent, syncSkillContent, threatModelSkillContent, vscodeHooksContent } from "./rules.js";
|
|
5
|
+
export async function scaffoldVSCode(cwd, config) {
|
|
6
|
+
const paths = {
|
|
7
|
+
guardrailsSelectionSkillDir: ".github/skills/guardrails-selection",
|
|
8
|
+
threatModellingSkillDir: ".github/skills/threat-modelling",
|
|
9
|
+
vibereviewSyncSkillDir: ".github/skills/vibereview-sync",
|
|
10
|
+
};
|
|
11
|
+
const settingsPath = join(cwd, ".vscode", "settings.json");
|
|
12
|
+
const existing = (await readJson(settingsPath)) ?? {};
|
|
13
|
+
await writeJson(settingsPath, {
|
|
14
|
+
...existing,
|
|
15
|
+
"vibreview.serverUrl": config.server_url,
|
|
16
|
+
"vibreview.projectSlug": config.project_slug,
|
|
17
|
+
});
|
|
18
|
+
const mcpPath = join(cwd, ".vscode", "mcp.json");
|
|
19
|
+
const mcpConfig = (await readJson(mcpPath)) ?? {};
|
|
20
|
+
const servers = mcpConfig.servers ?? {};
|
|
21
|
+
servers.vibreview = mcpRemoteServerWithType(config);
|
|
22
|
+
await writeJson(mcpPath, { ...mcpConfig, servers });
|
|
23
|
+
await upsertBlock(join(cwd, ".github", "copilot-instructions.md"), SENTINEL_START, SENTINEL_END, copilotRuleContent(config));
|
|
24
|
+
await writeText(join(cwd, ".github", "hooks", "vibreview-session-policy.json"), vscodeHooksContent(config));
|
|
25
|
+
await writeText(join(cwd, ".github", "skills", "guardrails-selection", "SKILL.md"), `${guardrailsSelectionSkillContent(config, paths)}\n`);
|
|
26
|
+
await writeText(join(cwd, ".github", "skills", "threat-modelling", "SKILL.md"), `${threatModelSkillContent(config, paths)}\n`);
|
|
27
|
+
await writeText(join(cwd, ".github", "skills", "vibereview-sync", "SKILL.md"), `${syncSkillContent(config)}\n`);
|
|
28
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { readJson, writeJson } from "../fs.js";
|
|
3
|
+
import { mcpRemoteServer } from "./mcp.js";
|
|
4
|
+
export async function scaffoldWindsurf(cwd, config) {
|
|
5
|
+
const path = join(cwd, ".windsurf", "mcp_config.json");
|
|
6
|
+
const existing = (await readJson(path)) ?? {};
|
|
7
|
+
const mcpServers = existing.mcpServers ?? {};
|
|
8
|
+
mcpServers.vibreview = mcpRemoteServer(config);
|
|
9
|
+
await writeJson(path, { ...existing, mcpServers });
|
|
10
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { VibeReviewApiClient } from "../api.js";
|
|
4
|
+
import { SCANS_DIR, readConfig, writeConfig } from "../config.js";
|
|
5
|
+
import { buildSyncMarkdownPayload } from "./payload.js";
|
|
6
|
+
import { readSyncState, writeSyncState } from "./state.js";
|
|
7
|
+
export async function syncTelemetry(cwd, options = {}) {
|
|
8
|
+
const config = await readConfig(cwd);
|
|
9
|
+
if (!config)
|
|
10
|
+
throw new Error("VibeReview is not initialized. Run `securityreview-kit init` first.");
|
|
11
|
+
const scanDir = join(cwd, SCANS_DIR);
|
|
12
|
+
const files = (await readdir(scanDir).catch(() => [])).filter((file) => file.endsWith(".md")).sort();
|
|
13
|
+
const state = await readSyncState(cwd);
|
|
14
|
+
const client = new VibeReviewApiClient(config);
|
|
15
|
+
let synced = 0;
|
|
16
|
+
let skipped = 0;
|
|
17
|
+
for (const file of files) {
|
|
18
|
+
const path = join(scanDir, file);
|
|
19
|
+
const content = await readFile(path, "utf8");
|
|
20
|
+
const payload = buildSyncMarkdownPayload(config.project_slug, path, content, config.ide[0]);
|
|
21
|
+
const previous = state.artifacts[payload.artifact_id];
|
|
22
|
+
if (!options.force && previous?.artifact_hash === payload.artifact_hash) {
|
|
23
|
+
skipped++;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
await client.syncCTMMarkdown(config.project_slug, payload);
|
|
27
|
+
const syncedAt = new Date().toISOString();
|
|
28
|
+
state.artifacts[payload.artifact_id] = { artifact_hash: payload.artifact_hash, synced_at: syncedAt };
|
|
29
|
+
synced++;
|
|
30
|
+
}
|
|
31
|
+
await writeSyncState(cwd, state);
|
|
32
|
+
await writeConfig(cwd, { ...config, last_synced: new Date().toISOString() });
|
|
33
|
+
return { synced, skipped };
|
|
34
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { basename } from "node:path";
|
|
3
|
+
export function artifactHash(content) {
|
|
4
|
+
return createHash("sha256").update(content).digest("hex");
|
|
5
|
+
}
|
|
6
|
+
export function buildSyncMarkdownPayload(projectSlug, filePath, content, ide) {
|
|
7
|
+
const hash = artifactHash(content);
|
|
8
|
+
const artifactId = basename(filePath).replace(/\.md$/i, "");
|
|
9
|
+
return {
|
|
10
|
+
markdown: content,
|
|
11
|
+
session_id: artifactId || randomUUID(),
|
|
12
|
+
artifact_id: artifactId || hash.slice(0, 16),
|
|
13
|
+
artifact_hash: hash,
|
|
14
|
+
idempotency_key: `${projectSlug}:${artifactId}:${hash}`,
|
|
15
|
+
generated_at: new Date().toISOString(),
|
|
16
|
+
file_name: basename(filePath),
|
|
17
|
+
ide,
|
|
18
|
+
metadata: {
|
|
19
|
+
source: "securityreview-kit",
|
|
20
|
+
file_path: filePath,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { SYNC_STATE_PATH } from "../config.js";
|
|
3
|
+
import { readJson, writeJson } from "../fs.js";
|
|
4
|
+
export async function readSyncState(cwd) {
|
|
5
|
+
return ((await readJson(join(cwd, SYNC_STATE_PATH))) ?? {
|
|
6
|
+
version: 1,
|
|
7
|
+
artifacts: {},
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
export async function writeSyncState(cwd, state) {
|
|
11
|
+
await writeJson(join(cwd, SYNC_STATE_PATH), state);
|
|
12
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,44 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@securityreviewai/securityreview-kit",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Bootstrap security-review-mcp for AI IDEs and CLI tools",
|
|
5
|
-
"author": "Debarshi Das <debarshi.das@we45.com>",
|
|
6
|
-
"license": "UNLICENSED",
|
|
3
|
+
"version": "0.1.50",
|
|
7
4
|
"type": "module",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
8
|
"bin": {
|
|
9
|
-
"securityreview-kit": "./
|
|
9
|
+
"securityreview-kit": "./dist/index.js",
|
|
10
|
+
"vibreview": "./dist/index.js"
|
|
10
11
|
},
|
|
11
12
|
"files": [
|
|
12
|
-
"
|
|
13
|
-
"
|
|
13
|
+
"dist",
|
|
14
|
+
"templates",
|
|
14
15
|
"README.md"
|
|
15
16
|
],
|
|
16
|
-
"engines": {
|
|
17
|
-
"node": ">=18"
|
|
18
|
-
},
|
|
19
17
|
"scripts": {
|
|
20
|
-
"
|
|
21
|
-
"
|
|
18
|
+
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"dev": "tsx src/index.ts",
|
|
20
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
21
|
+
"test": "vitest run"
|
|
22
22
|
},
|
|
23
|
-
"keywords": [
|
|
24
|
-
"security",
|
|
25
|
-
"mcp",
|
|
26
|
-
"security-review",
|
|
27
|
-
"srai",
|
|
28
|
-
"ai-ide",
|
|
29
|
-
"cursor",
|
|
30
|
-
"claude",
|
|
31
|
-
"codex",
|
|
32
|
-
"gemini",
|
|
33
|
-
"windsurf",
|
|
34
|
-
"vscode"
|
|
35
|
-
],
|
|
36
23
|
"dependencies": {
|
|
37
|
-
"chalk": "^5.
|
|
38
|
-
"commander": "^
|
|
39
|
-
"inquirer": "^12.
|
|
24
|
+
"chalk": "^5.6.2",
|
|
25
|
+
"commander": "^14.0.2",
|
|
26
|
+
"inquirer": "^12.10.0",
|
|
27
|
+
"zod": "^4.4.3"
|
|
40
28
|
},
|
|
41
|
-
"
|
|
42
|
-
"
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^25.6.0",
|
|
31
|
+
"tsx": "^4.21.0",
|
|
32
|
+
"typescript": "^5.9.3",
|
|
33
|
+
"vitest": "^4.1.5"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=20"
|
|
43
37
|
}
|
|
44
38
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# VibeReview Security Workflow
|
|
2
|
+
|
|
3
|
+
When implementing or changing code in this repository:
|
|
4
|
+
|
|
5
|
+
1. Resolve the current tenant/project with `vibreview_get_tenant_project`.
|
|
6
|
+
2. Fetch the project's guardrail bundle with `vibreview_get_guardrails`.
|
|
7
|
+
3. Locally shortlist the guardrails relevant to the user's requested feature or change.
|
|
8
|
+
4. Before editing, write a concise threat model for the requested change.
|
|
9
|
+
5. Implement the change while preserving the shortlisted guardrails.
|
|
10
|
+
6. Record the review/threat-model outcome as one markdown artifact in `.vibreview/scans/`.
|
|
11
|
+
7. Sync the markdown artifact with `vibreview_ctm_sync_markdown`.
|
|
12
|
+
|
|
13
|
+
Do not run local repository profiling from the IDE. VibeReview profiles the repository server-side after project creation and stores the relevant guardrails for this tenant/project in the MCP-backed KV cache.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Guardrail Selector
|
|
2
|
+
|
|
3
|
+
Use this agent when a task needs careful guardrail selection before code changes.
|
|
4
|
+
|
|
5
|
+
Do not profile the local repository from the IDE. VibeReview SaaS profiles repositories server-side after project creation and projects the required guardrails into the MCP/KV path.
|
|
6
|
+
|
|
7
|
+
Workflow:
|
|
8
|
+
|
|
9
|
+
1. Call `vibreview_get_tenant_project`.
|
|
10
|
+
2. Call `vibreview_get_guardrails`.
|
|
11
|
+
3. Rank guardrails by direct relevance to the user request, changed files, data handled, entry points, auth/authorization boundaries, and external integrations.
|
|
12
|
+
4. Return the shortlisted guardrail IDs, names, and the reason each one applies.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Threat Modeler
|
|
2
|
+
|
|
3
|
+
Placeholder agent definition for future VibeReview threat modeling automation.
|
|
4
|
+
|
|
5
|
+
Before implementation, use `vibreview_get_tenant_project` and `vibreview_get_guardrails`, shortlist the relevant guardrails locally, and identify likely threats, mitigations, standards mappings, and residual risk. Keep the model concise enough to sync as one markdown artifact with `vibreview_ctm_sync_markdown`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# VibeReview Skill
|
|
2
|
+
|
|
3
|
+
Use this skill for VibeReview-aware secure coding sessions.
|
|
4
|
+
|
|
5
|
+
## Fast Sync Rules
|
|
6
|
+
|
|
7
|
+
- Write one telemetry artifact per task in `.vibreview/scans/`.
|
|
8
|
+
- Keep the artifact readable markdown: task, threat model, selected guardrails, decisions, and follow-up risks.
|
|
9
|
+
- Avoid pasting large diffs or full files into telemetry unless necessary.
|
|
10
|
+
- Include guardrail IDs, affected files, and concise evidence when useful.
|
|
11
|
+
- Call `vibreview_ctm_sync_markdown` immediately after implementation or threat model updates.
|
|
12
|
+
|
|
13
|
+
## Workflow
|
|
14
|
+
|
|
15
|
+
1. Load tenant/project context with `vibreview_get_tenant_project`.
|
|
16
|
+
2. Fetch guardrails with `vibreview_get_guardrails`.
|
|
17
|
+
3. Filter and rank the returned guardrails locally for the requested feature.
|
|
18
|
+
4. Threat model before editing.
|
|
19
|
+
5. Implement with guardrails applied.
|
|
20
|
+
6. Record markdown telemetry.
|
|
21
|
+
7. Sync with `vibreview_ctm_sync_markdown`.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Guardrail Patterns
|
|
2
|
+
|
|
3
|
+
Fetch the project guardrail bundle with `vibreview_get_guardrails`, then filter locally against the feature request, touched files, framework, data flow, and threat model.
|
|
4
|
+
|
|
5
|
+
- Authentication: login, sessions, password handling, MFA, tokens.
|
|
6
|
+
- Authorization: role checks, object ownership, tenant isolation.
|
|
7
|
+
- Persistence: database queries, ORM usage, migrations.
|
|
8
|
+
- Validation: input parsing, bounds, schemas, file uploads.
|
|
9
|
+
- Data Exposure: logs, errors, secrets, sensitive output.
|
|
10
|
+
- Transport: TLS, headers, callbacks, API clients.
|
|
11
|
+
|
|
12
|
+
For broad changes, build a shortlist before editing. Prefer guardrails with direct category, title, instruction, severity, or metadata matches. Keep the selected guardrail IDs in the `.vibreview/scans/` markdown artifact so VibeReview can connect the IDE-side work back to the server-side guardrail set.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: VibeReview security workflow
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Before security-relevant code changes, use `vibreview_get_tenant_project` to resolve the tenant/project, then call `vibreview_get_guardrails` to fetch the project guardrail bundle. Filter and rank the returned guardrails locally against the user's requested feature before implementing.
|
|
7
|
+
|
|
8
|
+
Record one readable markdown artifact in `.vibreview/scans/` with the threat model, selected guardrail IDs, decisions, and important evidence. Sync it with `vibreview_ctm_sync_markdown`. Do not run local repository profiling; VibeReview handles profiling server-side after project creation.
|
package/README.md
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# @securityreviewai/securityreview-kit
|
|
2
|
-
|
|
3
|
-
> Bootstrap [security-review-mcp](https://www.npmjs.com/package/security-review-mcp) for AI IDEs and CLI tools in one command.
|
|
4
|
-
|
|
5
|
-
**@securityreviewai/securityreview-kit** configures the SRAI security review MCP server and installs workspace rules so your AI assistant consults security threat models and countermeasures *before* generating code.
|
|
6
|
-
|
|
7
|
-
## Quick Start
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# Interactive mode (recommended)
|
|
11
|
-
npx @securityreviewai/securityreview-kit init
|
|
12
|
-
|
|
13
|
-
# Or specify targets directly
|
|
14
|
-
npx @securityreviewai/securityreview-kit init --target cursor --api-url https://api.example.com --api-key YOUR_TOKEN
|
|
15
|
-
|
|
16
|
-
# Install for multiple targets
|
|
17
|
-
npx @securityreviewai/securityreview-kit init --target cursor claude vscode
|
|
18
|
-
|
|
19
|
-
# Install for all supported targets
|
|
20
|
-
npx @securityreviewai/securityreview-kit init --all --api-url https://api.example.com --api-key YOUR_TOKEN
|
|
21
|
-
|
|
22
|
-
# Re-open project selection menu and update installed rules
|
|
23
|
-
npx @securityreviewai/securityreview-kit init --switch-project
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Supported Targets
|
|
27
|
-
|
|
28
|
-
| Target | Flag | MCP Config | Workspace Rule |
|
|
29
|
-
|---|---|---|---|
|
|
30
|
-
| Cursor | `cursor` | `.cursor/mcp.json` | `.cursor/rules/srai-security-review.mdc`, `.cursor/rules/guardrails_rule.mdc`, `.cursor/commands/srai-profile.md`, `.cursor/commands/guardrails-init-profile.md`, `.cursor/skills/threat-modelling/SKILL.md`, `.cursor/skills/vibereview-sync/SKILL.md`, `.cursor/hooks.json` |
|
|
31
|
-
| Claude Code | `claude` | `.mcp.json` | `.claude/CLAUDE.md`, `.claude/settings.json`, `.claude/skills/threat-modelling/SKILL.md`, `.claude/skills/vibereview-sync/SKILL.md`, `.claude/skills/guardrails-profiler/SKILL.md`, `.claude/skills/guardrails-selection/SKILL.md`, `.claude/commands/guardrails-init-profile.md` |
|
|
32
|
-
| VS Code Copilot | `vscode` | `.vscode/mcp.json` | `.github/copilot-instructions.md`, `.github/skills/threat-modelling/SKILL.md`, `.github/skills/vibereview-sync/SKILL.md`, `.github/skills/guardrails-profiler/SKILL.md`, `.github/skills/guardrails-selection/SKILL.md`, `.github/hooks/srai-session-policy.json` |
|
|
33
|
-
| Windsurf | `windsurf` | `.windsurf/mcp_config.json` | `.windsurf/rules/srai-security-review.md` |
|
|
34
|
-
| Codex | `codex` | `.codex/config.toml` | `.codex/AGENTS.md`, `.codex/skills/threat-modelling/SKILL.md`, `.codex/skills/vibereview-sync/SKILL.md`, `.codex/skills/guardrails-profiler/SKILL.md`, `.codex/skills/guardrails-selection/SKILL.md`, `.codex/hooks.json`, `.codex/commands/guardrails-init-profile.md` |
|
|
35
|
-
| Gemini CLI | `gemini` | `.gemini/settings.json` | `GEMINI.md` |
|
|
36
|
-
| Antigravity | `antigravity` | `.gemini/settings.json` | `.agents/rules/srai-security-review.md` |
|
|
37
|
-
|
|
38
|
-
## Commands
|
|
39
|
-
|
|
40
|
-
### `@securityreviewai/securityreview-kit init`
|
|
41
|
-
|
|
42
|
-
Configure security-review-mcp for your IDE/CLI. Runs interactively when no flags are provided.
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
Options:
|
|
46
|
-
-t, --target <name...> Target IDE/CLI (cursor, claude, vscode, windsurf, codex, gemini, antigravity)
|
|
47
|
-
-a, --all Install for all supported targets
|
|
48
|
-
--project-name <name> (Optional) Preselect project name from fetched API project list
|
|
49
|
-
--api-url <url> SRAI API URL (or set SECURITY_REVIEW_API_URL env var)
|
|
50
|
-
--api-key <token> SRAI API Token (or set SECURITY_REVIEW_API_TOKEN env var)
|
|
51
|
-
--switch-project Fetch projects and only update mapped workspace rules
|
|
52
|
-
--skip-mcp Skip MCP server config installation
|
|
53
|
-
--skip-rules Skip workspace rule installation
|
|
54
|
-
--profile-repo Run the guardrails profiler after init
|
|
55
|
-
--profiler-claude-login Run Claude Code login before profiling
|
|
56
|
-
--claude-auth-mode <mode>
|
|
57
|
-
Claude profiling auth mode: current, claudeai, console, api_key, gateway, bedrock, vertex, or setup_token
|
|
58
|
-
--claude-api-key <key> Anthropic API key for Claude profiling
|
|
59
|
-
--claude-base-url <url> Anthropic-compatible base URL for Claude profiling
|
|
60
|
-
--claude-auth-token <token>
|
|
61
|
-
Auth token for Claude profiling gateway mode
|
|
62
|
-
--claude-provider-model <model>
|
|
63
|
-
Optional Claude provider model override for gateway, Bedrock, or Vertex profiling
|
|
64
|
-
--profiler-copilot-login
|
|
65
|
-
Run GitHub Copilot CLI login before VS Code Copilot profiling
|
|
66
|
-
--profiler-codex-login Run Codex login before Codex profiling
|
|
67
|
-
--profiler-verbose Show live profiler output while profiling runs
|
|
68
|
-
--show-profiler-logs Alias for --profiler-verbose
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### `@securityreviewai/securityreview-kit init --switch-project`
|
|
72
|
-
|
|
73
|
-
Fetches projects from `https://<api-url>/api/projects/` using `Authorization: Bearer <api-key>`, shows a single-select menu, and updates installed workspace rules with the selected project.
|
|
74
|
-
|
|
75
|
-
### `@securityreviewai/securityreview-kit status`
|
|
76
|
-
|
|
77
|
-
Show current configuration status for all supported targets in the workspace.
|
|
78
|
-
|
|
79
|
-
## Environment Variables
|
|
80
|
-
|
|
81
|
-
| Variable | Description |
|
|
82
|
-
|---|---|
|
|
83
|
-
| `SECURITY_REVIEW_PROJECT_NAME` | Optional default project name to preselect in the project menu |
|
|
84
|
-
| `SECURITY_REVIEW_API_URL` | SRAI platform API endpoint |
|
|
85
|
-
| `SECURITY_REVIEW_API_TOKEN` | Your SRAI API token |
|
|
86
|
-
|
|
87
|
-
These can be provided via CLI flags, environment variables, or interactive prompts.
|
|
88
|
-
|
|
89
|
-
## What Gets Installed
|
|
90
|
-
|
|
91
|
-
**MCP Server Config** — tells your IDE how to launch the `security-review-mcp` server via `npx`.
|
|
92
|
-
|
|
93
|
-
**Workspace Rules** — instructs the AI assistant to consult SRAI threat models and countermeasures before generating security-relevant code. If configured, the selected SRAI project name is injected into the MCP workflow instructions in the installed rule content.
|
|
94
|
-
|
|
95
|
-
## How It Works
|
|
96
|
-
|
|
97
|
-
1. Run `@securityreviewai/securityreview-kit init`
|
|
98
|
-
2. Select your IDE/CLI target(s)
|
|
99
|
-
3. Choose whether to install workspace rules and MCP config
|
|
100
|
-
4. If MCP is selected, enter your SRAI credentials (API URL, token)
|
|
101
|
-
5. The tool fetches `/api/projects/` and you select exactly one SRAI project from the menu
|
|
102
|
-
6. The tool creates/merges MCP config and workspace rule files
|
|
103
|
-
7. Your AI assistant now has access to SRAI security reviews
|
|
104
|
-
|
|
105
|
-
The tool is **idempotent** — running it multiple times safely updates existing configs without duplicating content.
|