@securityreviewai/securityreview-kit 0.1.49 → 0.1.51
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/scaffold/claude-code.js +12 -6
- package/dist/scaffold/codex.js +11 -5
- package/dist/scaffold/cursor.js +13 -7
- package/dist/scaffold/rules.js +117 -91
- package/dist/scaffold/vibreview.js +17 -11
- package/dist/scaffold/vscode.js +11 -5
- package/package.json +1 -1
- package/templates/shared/content.md +60 -0
- package/templates/shared/guardrails-selection.md +185 -0
- package/templates/shared/threat-modelling.md +259 -0
- package/templates/shared/vibereview-sync/SKILL.md +381 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { readJson, writeJson, writeText, upsertBlock } from "../fs.js";
|
|
3
3
|
import { mcpRemoteServerWithType } from "./mcp.js";
|
|
4
|
-
import { SENTINEL_END, SENTINEL_START, claudeRuleContent, syncSkillContent, threatModelSkillContent } from "./rules.js";
|
|
4
|
+
import { SENTINEL_END, SENTINEL_START, claudeRuleContent, guardrailsSelectionSkillContent, syncSkillContent, threatModelSkillContent } from "./rules.js";
|
|
5
5
|
export async function scaffoldClaudeCode(cwd, config) {
|
|
6
6
|
await writeMcpConfig(cwd, config);
|
|
7
7
|
await writeClaudeSettings(cwd);
|
|
8
|
-
await writeClaudeRules(cwd);
|
|
8
|
+
await writeClaudeRules(cwd, config);
|
|
9
9
|
}
|
|
10
10
|
async function writeMcpConfig(cwd, config) {
|
|
11
11
|
const path = join(cwd, ".mcp.json");
|
|
@@ -30,8 +30,14 @@ async function writeClaudeSettings(cwd) {
|
|
|
30
30
|
},
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
-
async function writeClaudeRules(cwd) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
async function writeClaudeRules(cwd, config) {
|
|
34
|
+
const paths = {
|
|
35
|
+
guardrailsSelectionSkillDir: ".claude/skills/guardrails-selection",
|
|
36
|
+
threatModellingSkillDir: ".claude/skills/threat-modelling",
|
|
37
|
+
vibereviewSyncSkillDir: ".claude/skills/vibereview-sync",
|
|
38
|
+
};
|
|
39
|
+
await upsertBlock(join(cwd, "CLAUDE.md"), SENTINEL_START, SENTINEL_END, claudeRuleContent(config));
|
|
40
|
+
await writeText(join(cwd, ".claude", "skills", "guardrails-selection", "SKILL.md"), `${guardrailsSelectionSkillContent(config, paths)}\n`);
|
|
41
|
+
await writeText(join(cwd, ".claude", "skills", "threat-modelling", "SKILL.md"), `${threatModelSkillContent(config, paths)}\n`);
|
|
42
|
+
await writeText(join(cwd, ".claude", "skills", "vibereview-sync", "SKILL.md"), `${syncSkillContent(config)}\n`);
|
|
37
43
|
}
|
package/dist/scaffold/codex.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { readText, upsertBlock, writeText } from "../fs.js";
|
|
3
|
-
import { SENTINEL_END, SENTINEL_START, codexHooksContent, codexRuleContent, syncSkillContent, threatModelSkillContent } from "./rules.js";
|
|
3
|
+
import { SENTINEL_END, SENTINEL_START, codexHooksContent, codexRuleContent, guardrailsSelectionSkillContent, syncSkillContent, threatModelSkillContent } from "./rules.js";
|
|
4
4
|
export async function scaffoldCodex(cwd, config) {
|
|
5
|
+
const paths = {
|
|
6
|
+
guardrailsSelectionSkillDir: ".codex/skills/guardrails-selection",
|
|
7
|
+
threatModellingSkillDir: ".codex/skills/threat-modelling",
|
|
8
|
+
vibereviewSyncSkillDir: ".codex/skills/vibereview-sync",
|
|
9
|
+
};
|
|
5
10
|
await writeCodexConfig(cwd, config);
|
|
6
|
-
await upsertBlock(join(cwd, ".codex", "AGENTS.md"), SENTINEL_START, SENTINEL_END, codexRuleContent());
|
|
7
|
-
await writeText(join(cwd, ".codex", "hooks.json"), codexHooksContent());
|
|
8
|
-
await writeText(join(cwd, ".codex", "skills", "
|
|
9
|
-
await writeText(join(cwd, ".codex", "skills", "
|
|
11
|
+
await upsertBlock(join(cwd, ".codex", "AGENTS.md"), SENTINEL_START, SENTINEL_END, codexRuleContent(config));
|
|
12
|
+
await writeText(join(cwd, ".codex", "hooks.json"), codexHooksContent(config));
|
|
13
|
+
await writeText(join(cwd, ".codex", "skills", "guardrails-selection", "SKILL.md"), `${guardrailsSelectionSkillContent(config, paths)}\n`);
|
|
14
|
+
await writeText(join(cwd, ".codex", "skills", "threat-modelling", "SKILL.md"), `${threatModelSkillContent(config, paths)}\n`);
|
|
15
|
+
await writeText(join(cwd, ".codex", "skills", "vibereview-sync", "SKILL.md"), `${syncSkillContent(config)}\n`);
|
|
10
16
|
}
|
|
11
17
|
async function writeCodexConfig(cwd, config) {
|
|
12
18
|
const path = join(cwd, ".codex", "config.toml");
|
package/dist/scaffold/cursor.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { readJson, writeJson, writeText } from "../fs.js";
|
|
3
3
|
import { mcpRemoteServerWithType } from "./mcp.js";
|
|
4
|
-
import { cursorHooksContent, cursorRuleContent, syncSkillContent, threatModelSkillContent } from "./rules.js";
|
|
4
|
+
import { cursorHooksContent, cursorRuleContent, guardrailsSelectionSkillContent, syncSkillContent, threatModelSkillContent } from "./rules.js";
|
|
5
5
|
export async function scaffoldCursor(cwd, config) {
|
|
6
|
+
const paths = {
|
|
7
|
+
guardrailsSelectionSkillDir: ".cursor/skills/guardrails-selection",
|
|
8
|
+
threatModellingSkillDir: ".cursor/skills/threat-modelling",
|
|
9
|
+
vibereviewSyncSkillDir: ".cursor/skills/vibereview-sync",
|
|
10
|
+
};
|
|
6
11
|
const mcpPath = join(cwd, ".cursor", "mcp.json");
|
|
7
12
|
const existing = (await readJson(mcpPath)) ?? {};
|
|
8
13
|
const mcpServers = existing.mcpServers ?? {};
|
|
9
14
|
mcpServers.vibreview = mcpRemoteServerWithType(config);
|
|
10
15
|
await writeJson(mcpPath, { ...existing, mcpServers });
|
|
11
|
-
await writeText(join(cwd, ".cursor", "rules", "vibreview-security.mdc"), cursorRuleFile());
|
|
12
|
-
await writeText(join(cwd, ".cursor", "hooks.json"), cursorHooksContent());
|
|
13
|
-
await writeText(join(cwd, ".cursor", "skills", "
|
|
14
|
-
await writeText(join(cwd, ".cursor", "skills", "
|
|
16
|
+
await writeText(join(cwd, ".cursor", "rules", "vibreview-security.mdc"), cursorRuleFile(config));
|
|
17
|
+
await writeText(join(cwd, ".cursor", "hooks.json"), cursorHooksContent(config));
|
|
18
|
+
await writeText(join(cwd, ".cursor", "skills", "guardrails-selection", "SKILL.md"), `${guardrailsSelectionSkillContent(config, paths)}\n`);
|
|
19
|
+
await writeText(join(cwd, ".cursor", "skills", "threat-modelling", "SKILL.md"), `${threatModelSkillContent(config, paths)}\n`);
|
|
20
|
+
await writeText(join(cwd, ".cursor", "skills", "vibereview-sync", "SKILL.md"), `${syncSkillContent(config)}\n`);
|
|
15
21
|
await updateCursorAllowlist(cwd);
|
|
16
22
|
}
|
|
17
23
|
async function updateCursorAllowlist(cwd) {
|
|
@@ -28,12 +34,12 @@ async function updateCursorAllowlist(cwd) {
|
|
|
28
34
|
},
|
|
29
35
|
});
|
|
30
36
|
}
|
|
31
|
-
function cursorRuleFile() {
|
|
37
|
+
function cursorRuleFile(config) {
|
|
32
38
|
return `---
|
|
33
39
|
description: VibeReview security workflow
|
|
34
40
|
alwaysApply: true
|
|
35
41
|
---
|
|
36
42
|
|
|
37
|
-
${cursorRuleContent()}
|
|
43
|
+
${cursorRuleContent(config)}
|
|
38
44
|
`;
|
|
39
45
|
}
|
package/dist/scaffold/rules.js
CHANGED
|
@@ -1,101 +1,63 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
const TOOL_PROJECT = "`vibreview_get_tenant_project`";
|
|
2
3
|
const TOOL_GUARDRAILS = "`vibreview_get_guardrails`";
|
|
3
4
|
const TOOL_SYNC = "`vibreview_ctm_sync_markdown`";
|
|
4
5
|
export const SENTINEL_START = "<!-- securityreview-kit:start -->";
|
|
5
6
|
export const SENTINEL_END = "<!-- securityreview-kit:end -->";
|
|
6
|
-
export function
|
|
7
|
-
return
|
|
8
|
-
"# VibeReview Security Workflow",
|
|
9
|
-
"",
|
|
10
|
-
"For any security-relevant task touching auth, authorization, input handling, secrets, network, storage, dependencies, new endpoints, infrastructure, or untrusted data:",
|
|
11
|
-
"",
|
|
12
|
-
`1. Resolve the current tenant/project with ${TOOL_PROJECT}.`,
|
|
13
|
-
`2. Fetch the project guardrail bundle with ${TOOL_GUARDRAILS}, shortlist only the guardrails relevant to the task, and keep that shortlist in context.`,
|
|
14
|
-
"3. Run threat modelling before implementation and cross-check the shortlist against the likely abuse paths.",
|
|
15
|
-
"4. Implement secure code using the shortlisted guardrails as hard constraints.",
|
|
16
|
-
`5. Write one markdown artifact under \`.vibreview/scans/\` and call ${TOOL_SYNC} immediately after writing or updating it.`,
|
|
17
|
-
"",
|
|
18
|
-
"Do not run local profiling from the IDE. Repository profiling happens server-side after project creation.",
|
|
19
|
-
].join("\n");
|
|
7
|
+
export function ruleContent(config, paths) {
|
|
8
|
+
return renderTemplate("content.md", config, paths);
|
|
20
9
|
}
|
|
21
|
-
export function
|
|
22
|
-
return
|
|
23
|
-
|
|
24
|
-
"",
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
`After the security review or implementation step, write one markdown artifact under \`.vibreview/scans/\` and call ${TOOL_SYNC} immediately. If sync fails, keep the file on disk and report the failure clearly.`,
|
|
28
|
-
].join("\n");
|
|
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
|
+
});
|
|
29
16
|
}
|
|
30
|
-
export function
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
"",
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
`After threat modelling or implementation, write one concise markdown artifact under \`.vibreview/scans/\` and call ${TOOL_SYNC} right away. The same agent that did the work should author the markdown so the context stays grounded.`,
|
|
37
|
-
].join("\n");
|
|
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
|
+
});
|
|
38
23
|
}
|
|
39
|
-
export function
|
|
40
|
-
return
|
|
41
|
-
"
|
|
42
|
-
"",
|
|
43
|
-
"
|
|
44
|
-
|
|
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
|
+
});
|
|
45
30
|
}
|
|
46
|
-
export function
|
|
47
|
-
return
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
].join("\n");
|
|
64
|
-
}
|
|
65
|
-
export function syncSkillContent(scanDir = ".vibreview/scans") {
|
|
66
|
-
return [
|
|
67
|
-
"---",
|
|
68
|
-
"name: vibereview-sync",
|
|
69
|
-
"description: Write and synchronize a VibeReview markdown artifact for the current security-relevant task.",
|
|
70
|
-
"---",
|
|
71
|
-
"",
|
|
72
|
-
"# VibeReview Markdown Sync",
|
|
73
|
-
"",
|
|
74
|
-
`Write one markdown artifact under \`${scanDir}/\` for the current task only.`,
|
|
75
|
-
"",
|
|
76
|
-
"Rules:",
|
|
77
|
-
`1. The artifact must live under \`${scanDir}/\`.`,
|
|
78
|
-
"2. Do not read sibling markdown files just to infer structure or reuse content.",
|
|
79
|
-
"3. Include the threat model, selected guardrail IDs, important decisions, and grounded code evidence when useful.",
|
|
80
|
-
"4. If updating an existing task artifact, update that file directly instead of creating duplicates.",
|
|
81
|
-
`5. Call ${TOOL_SYNC} immediately after writing or updating the markdown.`,
|
|
82
|
-
"6. If sync fails, leave the markdown file on disk and report the failure clearly.",
|
|
83
|
-
].join("\n");
|
|
84
|
-
}
|
|
85
|
-
export function cursorHooksContent() {
|
|
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) {
|
|
86
48
|
return JSON.stringify({
|
|
87
49
|
version: 1,
|
|
88
50
|
hooks: {
|
|
89
51
|
sessionStart: [
|
|
90
52
|
{
|
|
91
|
-
command: `printf '%s\\n' '${jsonPayload(
|
|
53
|
+
command: `printf '%s\\n' '${jsonPayload(additionalContext(config)).replaceAll("'", "'\\''")}'`,
|
|
92
54
|
timeout: 5,
|
|
93
55
|
},
|
|
94
56
|
],
|
|
95
57
|
},
|
|
96
58
|
}, null, 2) + "\n";
|
|
97
59
|
}
|
|
98
|
-
export function codexHooksContent() {
|
|
60
|
+
export function codexHooksContent(config) {
|
|
99
61
|
return JSON.stringify({
|
|
100
62
|
hooks: {
|
|
101
63
|
SessionStart: [
|
|
@@ -104,7 +66,7 @@ export function codexHooksContent() {
|
|
|
104
66
|
hooks: [
|
|
105
67
|
{
|
|
106
68
|
type: "command",
|
|
107
|
-
command: `printf '%s\\n' '${codexPayload("SessionStart").replaceAll("'", "'\\''")}'`,
|
|
69
|
+
command: `printf '%s\\n' '${codexPayload(config, "SessionStart").replaceAll("'", "'\\''")}'`,
|
|
108
70
|
timeout: 5,
|
|
109
71
|
statusMessage: "Loading VibeReview security session policy",
|
|
110
72
|
},
|
|
@@ -116,7 +78,7 @@ export function codexHooksContent() {
|
|
|
116
78
|
hooks: [
|
|
117
79
|
{
|
|
118
80
|
type: "command",
|
|
119
|
-
command: `printf '%s\\n' '${codexPayload("UserPromptSubmit").replaceAll("'", "'\\''")}'`,
|
|
81
|
+
command: `printf '%s\\n' '${codexPayload(config, "UserPromptSubmit").replaceAll("'", "'\\''")}'`,
|
|
120
82
|
timeout: 5,
|
|
121
83
|
statusMessage: "Refreshing VibeReview security session policy",
|
|
122
84
|
},
|
|
@@ -126,40 +88,104 @@ export function codexHooksContent() {
|
|
|
126
88
|
},
|
|
127
89
|
}, null, 2) + "\n";
|
|
128
90
|
}
|
|
129
|
-
export function vscodeHooksContent() {
|
|
91
|
+
export function vscodeHooksContent(config) {
|
|
130
92
|
return JSON.stringify({
|
|
131
93
|
hooks: {
|
|
132
94
|
SessionStart: [
|
|
133
95
|
{
|
|
134
96
|
type: "command",
|
|
135
|
-
command: `printf '%s\\n' '${codexPayload("SessionStart").replaceAll("'", "'\\''")}'`,
|
|
97
|
+
command: `printf '%s\\n' '${codexPayload(config, "SessionStart").replaceAll("'", "'\\''")}'`,
|
|
136
98
|
timeout: 5,
|
|
137
99
|
},
|
|
138
100
|
],
|
|
139
101
|
},
|
|
140
102
|
}, null, 2) + "\n";
|
|
141
103
|
}
|
|
142
|
-
function
|
|
104
|
+
function additionalContext(config) {
|
|
105
|
+
const project = projectContext(config);
|
|
143
106
|
return [
|
|
144
107
|
"## MANDATORY VIBEREVIEW SECURITY GATE",
|
|
145
108
|
"",
|
|
146
|
-
`
|
|
147
|
-
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
`
|
|
109
|
+
`Configured project: ${project.name} (${project.slug}, ${project.id})`,
|
|
110
|
+
"",
|
|
111
|
+
"For any request with security impact, this is a hard pre-write gate. Do not create, edit, or patch code until steps 1-3 are complete:",
|
|
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} with the raw markdown string immediately after writing it.`,
|
|
151
118
|
"",
|
|
152
|
-
"No deferral. No
|
|
119
|
+
"No deferral. No code before guardrails. No JSON summaries for sync. No local profiling. The main agent owns the final markdown sync.",
|
|
153
120
|
].join("\\n");
|
|
154
121
|
}
|
|
155
|
-
function codexPayload(hookEventName) {
|
|
122
|
+
function codexPayload(config, hookEventName) {
|
|
156
123
|
return JSON.stringify({
|
|
157
124
|
hookSpecificOutput: {
|
|
158
125
|
hookEventName,
|
|
159
|
-
additionalContext:
|
|
126
|
+
additionalContext: additionalContext(config),
|
|
160
127
|
},
|
|
161
128
|
});
|
|
162
129
|
}
|
|
163
130
|
function jsonPayload(additionalContext) {
|
|
164
131
|
return JSON.stringify({ additional_context: additionalContext });
|
|
165
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
|
+
}
|
|
@@ -10,15 +10,21 @@ export async function scaffoldVibeReview(cwd, config) {
|
|
|
10
10
|
const existingSyncState = await readJson(join(cwd, SYNC_STATE_PATH));
|
|
11
11
|
if (!existingSyncState)
|
|
12
12
|
await writeJson(join(cwd, SYNC_STATE_PATH), { version: 1, artifacts: {} });
|
|
13
|
-
await upsertBlock(join(cwd, ".gitignore"), GITIGNORE_START, GITIGNORE_END,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
".
|
|
19
|
-
|
|
20
|
-
".
|
|
21
|
-
|
|
22
|
-
".
|
|
23
|
-
|
|
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;
|
|
24
30
|
}
|
package/dist/scaffold/vscode.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { readJson, writeJson, writeText, upsertBlock } from "../fs.js";
|
|
3
3
|
import { mcpRemoteServerWithType } from "./mcp.js";
|
|
4
|
-
import { SENTINEL_END, SENTINEL_START, copilotRuleContent, syncSkillContent, threatModelSkillContent, vscodeHooksContent } from "./rules.js";
|
|
4
|
+
import { SENTINEL_END, SENTINEL_START, copilotRuleContent, guardrailsSelectionSkillContent, syncSkillContent, threatModelSkillContent, vscodeHooksContent } from "./rules.js";
|
|
5
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
|
+
};
|
|
6
11
|
const settingsPath = join(cwd, ".vscode", "settings.json");
|
|
7
12
|
const existing = (await readJson(settingsPath)) ?? {};
|
|
8
13
|
await writeJson(settingsPath, {
|
|
@@ -15,8 +20,9 @@ export async function scaffoldVSCode(cwd, config) {
|
|
|
15
20
|
const servers = mcpConfig.servers ?? {};
|
|
16
21
|
servers.vibreview = mcpRemoteServerWithType(config);
|
|
17
22
|
await writeJson(mcpPath, { ...mcpConfig, servers });
|
|
18
|
-
await upsertBlock(join(cwd, ".github", "copilot-instructions.md"), SENTINEL_START, SENTINEL_END, copilotRuleContent());
|
|
19
|
-
await writeText(join(cwd, ".github", "hooks", "vibreview-session-policy.json"), vscodeHooksContent());
|
|
20
|
-
await writeText(join(cwd, ".github", "skills", "
|
|
21
|
-
await writeText(join(cwd, ".github", "skills", "
|
|
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`);
|
|
22
28
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Security Review Kit Agent Instructions
|
|
2
|
+
|
|
3
|
+
Configured SRAI project name: `<SRAI_PROJECT_NAME>`
|
|
4
|
+
|
|
5
|
+
These instructions are always active for security-relevant coding work. Keep this file as the routing policy; use the skills and agents below for the detailed workflows.
|
|
6
|
+
|
|
7
|
+
## Core Workflow
|
|
8
|
+
|
|
9
|
+
For any task that touches auth, authorization, input handling, secrets, network, data storage, dependencies, new APIs/endpoints, infrastructure, or code handling untrusted data, this workflow is a hard pre-write gate.
|
|
10
|
+
|
|
11
|
+
Do not write, edit, patch, or generate implementation code until guardrails have been fetched and shortlisted and PWNISMS threat modelling has been completed. If the user asks for code immediately, still run the pre-write gate first.
|
|
12
|
+
|
|
13
|
+
1. **Fetch Vibe Guardrails first.**
|
|
14
|
+
- Read and follow `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md`.
|
|
15
|
+
- Resolve the SRAI project with `find_project_by_name` using `name="<SRAI_PROJECT_NAME>"`.
|
|
16
|
+
- Call `get_guardrails`, shortlist only the relevant project guardrails, then preserve the exact returned guardrail entries.
|
|
17
|
+
- Preserve that exact shortlist in context for implementation and the final VibeReview sync step.
|
|
18
|
+
|
|
19
|
+
2. **Run PWNISMS threat modelling before implementation.**
|
|
20
|
+
- Read and follow `{{THREAT_MODELLING_SKILL_DIR}}/SKILL.md`.
|
|
21
|
+
- Explicitly walk all seven PWNISMS categories: Product, Workload, Network, IAM, Secrets, Monitoring, and Supply Chain.
|
|
22
|
+
- If a category is not applicable, say so briefly and continue.
|
|
23
|
+
- Cross-reference each meaningful threat with the shortlisted guardrails.
|
|
24
|
+
|
|
25
|
+
3. **Implement secure code using both inputs.**
|
|
26
|
+
- This step may start only after steps 1 and 2 are complete.
|
|
27
|
+
- Treat applicable `must` guardrails as mandatory.
|
|
28
|
+
- Treat applicable `must_not` guardrails as hard prohibitions.
|
|
29
|
+
- Use the PWNISMS findings to guide design, validation, authorization, logging, secrets handling, dependency choices, and abuse controls.
|
|
30
|
+
- If PWNISMS reveals a recurring security rule that is not covered by existing guardrails, create and apply an `ide_generated` guardrail in context.
|
|
31
|
+
|
|
32
|
+
4. **Write and sync the VibeReview markdown last.**
|
|
33
|
+
- Read and follow `{{VIBEREVIEW_SYNC_SKILL_DIR}}/SKILL.md`.
|
|
34
|
+
- After threat modelling is created or updated, or after guardrails are enforced during implementation, the main agent must write or update a structured `.md` artifact under `.vibreview/scans/`.
|
|
35
|
+
- Do not delegate markdown authoring to a subagent. The same agent that performed the work should author the artifact so the context stays intact.
|
|
36
|
+
- Use the skill's structure and validation checklist rather than improvising the markdown format.
|
|
37
|
+
- Reuse the exact existing guardrails shortlisted earlier, include any `ide_generated` guardrails, and only include grounded code snippets from actual code.
|
|
38
|
+
- Call `sync_ai_ide_markdown` directly with the finished raw markdown content before finalizing the task. Do not sync JSON summaries, JSON objects, or extracted event JSON.
|
|
39
|
+
- If sync fails, keep the file in `.vibreview/scans/`, report the failure, and do not pretend synchronization succeeded.
|
|
40
|
+
|
|
41
|
+
## When To Skip
|
|
42
|
+
|
|
43
|
+
Skip the SRAI security workflow only for tasks with no security surface, such as documentation-only changes, typo fixes, pure formatting, variable renames with no logic change, or general Q&A that produces no code.
|
|
44
|
+
|
|
45
|
+
If in doubt, run the workflow. A quick PWNISMS pass is better than silently missing a security boundary.
|
|
46
|
+
|
|
47
|
+
## Do Not Use Project Profile Exploration
|
|
48
|
+
|
|
49
|
+
Do not call project-profile exploration tools during normal coding tasks. The old profile walkthrough is no longer part of the agent workflow.
|
|
50
|
+
|
|
51
|
+
The normal coding workflow is guardrails selection, PWNISMS threat modelling, secure implementation, then VibeReview markdown sync.
|
|
52
|
+
|
|
53
|
+
## Tool Reference
|
|
54
|
+
|
|
55
|
+
| Purpose | Tools |
|
|
56
|
+
|---|---|
|
|
57
|
+
| Project resolution | `find_project_by_name`, `list_projects`, `create_project`, `get_project` |
|
|
58
|
+
| Guardrails | `get_guardrails` |
|
|
59
|
+
| VibeReview sync | `sync_ai_ide_markdown` |
|
|
60
|
+
| Profiler only | `update_vibe_profile`, `write_default_pack` are used by init-time profiling, not normal coding tasks |
|