@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
package/src/utils/srai.js
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { MCP_SERVER_NAME, TARGET_NAMES, TARGETS } from './constants.js';
|
|
4
|
-
import { readJson, readText } from './fs-helpers.js';
|
|
5
|
-
|
|
6
|
-
const PROJECT_LIST_PATH = '/api/projects/';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Normalize API URL from raw input.
|
|
10
|
-
*/
|
|
11
|
-
export function normalizeApiUrl(value) {
|
|
12
|
-
const trimmed = String(value || '').trim();
|
|
13
|
-
if (!trimmed) return '';
|
|
14
|
-
|
|
15
|
-
if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
|
|
16
|
-
return trimmed;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return `https://${trimmed}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function parseTomlEnvValue(content, key) {
|
|
23
|
-
const match = content.match(new RegExp(`${key}\\s*=\\s*"(.*?)"`));
|
|
24
|
-
return match ? match[1].trim() : '';
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function extractCredentialsFromJsonConfig(content) {
|
|
28
|
-
const servers = content?.mcpServers || content?.servers;
|
|
29
|
-
if (!servers || typeof servers !== 'object') return null;
|
|
30
|
-
|
|
31
|
-
const server = servers[MCP_SERVER_NAME];
|
|
32
|
-
if (!server || typeof server !== 'object') return null;
|
|
33
|
-
|
|
34
|
-
const apiUrl = normalizeApiUrl(server?.env?.SECURITY_REVIEW_API_URL || '');
|
|
35
|
-
const apiToken = String(server?.env?.SECURITY_REVIEW_API_TOKEN || '').trim();
|
|
36
|
-
if (!apiUrl || !apiToken) return null;
|
|
37
|
-
|
|
38
|
-
return { apiUrl, apiToken };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Read previously saved API credentials from any configured MCP file.
|
|
43
|
-
*/
|
|
44
|
-
export function getStoredCredentials(cwd) {
|
|
45
|
-
for (const targetName of TARGET_NAMES) {
|
|
46
|
-
const target = TARGETS[targetName];
|
|
47
|
-
const mcpPath = join(cwd, target.mcpConfigPath);
|
|
48
|
-
if (!existsSync(mcpPath)) continue;
|
|
49
|
-
|
|
50
|
-
if (mcpPath.endsWith('.toml')) {
|
|
51
|
-
const content = readText(mcpPath);
|
|
52
|
-
if (!content) continue;
|
|
53
|
-
|
|
54
|
-
const apiUrl = normalizeApiUrl(parseTomlEnvValue(content, 'SECURITY_REVIEW_API_URL'));
|
|
55
|
-
const apiToken = parseTomlEnvValue(content, 'SECURITY_REVIEW_API_TOKEN');
|
|
56
|
-
if (apiUrl && apiToken) {
|
|
57
|
-
return { apiUrl, apiToken };
|
|
58
|
-
}
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const json = readJson(mcpPath);
|
|
63
|
-
const extracted = extractCredentialsFromJsonConfig(json);
|
|
64
|
-
if (extracted) {
|
|
65
|
-
return extracted;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return { apiUrl: '', apiToken: '' };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function resolveProjectsEndpoint(apiUrl) {
|
|
73
|
-
const parsed = new URL(apiUrl);
|
|
74
|
-
const pathname = parsed.pathname.replace(/\/+$/, '');
|
|
75
|
-
|
|
76
|
-
if (pathname.endsWith('/api/projects')) {
|
|
77
|
-
parsed.pathname = `${pathname}/`;
|
|
78
|
-
} else if (pathname.endsWith('/api')) {
|
|
79
|
-
parsed.pathname = `${pathname}/projects/`;
|
|
80
|
-
} else if (!pathname || pathname === '/') {
|
|
81
|
-
parsed.pathname = PROJECT_LIST_PATH;
|
|
82
|
-
} else {
|
|
83
|
-
parsed.pathname = `${pathname}${PROJECT_LIST_PATH}`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
parsed.search = '';
|
|
87
|
-
parsed.hash = '';
|
|
88
|
-
return parsed.toString();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function toProjectName(entry) {
|
|
92
|
-
if (typeof entry === 'string') return entry.trim();
|
|
93
|
-
if (!entry || typeof entry !== 'object') return '';
|
|
94
|
-
|
|
95
|
-
const nameFields = [
|
|
96
|
-
'name',
|
|
97
|
-
'projectName',
|
|
98
|
-
'project_name',
|
|
99
|
-
'displayName',
|
|
100
|
-
'display_name',
|
|
101
|
-
'title',
|
|
102
|
-
];
|
|
103
|
-
|
|
104
|
-
for (const key of nameFields) {
|
|
105
|
-
const value = entry[key];
|
|
106
|
-
if (typeof value === 'string' && value.trim()) {
|
|
107
|
-
return value.trim();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (entry.project && typeof entry.project === 'object') {
|
|
112
|
-
return toProjectName(entry.project);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return '';
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function collectProjectLists(payload) {
|
|
119
|
-
const collections = [];
|
|
120
|
-
|
|
121
|
-
if (Array.isArray(payload)) {
|
|
122
|
-
collections.push(payload);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (payload && typeof payload === 'object') {
|
|
126
|
-
const keys = ['projects', 'data', 'results', 'items'];
|
|
127
|
-
for (const key of keys) {
|
|
128
|
-
if (Array.isArray(payload[key])) {
|
|
129
|
-
collections.push(payload[key]);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return collections;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function extractProjectNames(payload) {
|
|
138
|
-
const names = new Set();
|
|
139
|
-
for (const list of collectProjectLists(payload)) {
|
|
140
|
-
for (const entry of list) {
|
|
141
|
-
const name = toProjectName(entry);
|
|
142
|
-
if (name) names.add(name);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return [...names].sort((a, b) => a.localeCompare(b));
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Whether a project object is marked for AI / vibe review kit selection.
|
|
151
|
-
* Only entries with an explicit true-like `is_vibe_review` (or common API aliases) qualify.
|
|
152
|
-
*/
|
|
153
|
-
export function isVibeReviewProject(entry) {
|
|
154
|
-
if (!entry || typeof entry !== 'object') return false;
|
|
155
|
-
|
|
156
|
-
const keys = ['is_vibe_review', 'isVibeReview', 'vibe_review', 'vibeReview'];
|
|
157
|
-
for (const key of keys) {
|
|
158
|
-
if (!Object.prototype.hasOwnProperty.call(entry, key)) continue;
|
|
159
|
-
const value = entry[key];
|
|
160
|
-
if (value === true || value === 1) return true;
|
|
161
|
-
if (typeof value === 'string') {
|
|
162
|
-
const v = value.toLowerCase().trim();
|
|
163
|
-
if (v === 'true' || v === '1' || v === 'yes') return true;
|
|
164
|
-
}
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Extract { name, entry } pairs from a projects API payload.
|
|
173
|
-
*/
|
|
174
|
-
export function extractProjectEntries(payload) {
|
|
175
|
-
const out = [];
|
|
176
|
-
const seen = new Set();
|
|
177
|
-
|
|
178
|
-
for (const list of collectProjectLists(payload)) {
|
|
179
|
-
for (const entry of list) {
|
|
180
|
-
const name = toProjectName(entry);
|
|
181
|
-
if (!name || seen.has(name)) continue;
|
|
182
|
-
seen.add(name);
|
|
183
|
-
out.push({ name, entry });
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
out.sort((a, b) => a.name.localeCompare(b.name));
|
|
188
|
-
return out;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
async function fetchProjectsPayload(apiUrl, apiToken) {
|
|
192
|
-
const endpoint = resolveProjectsEndpoint(apiUrl);
|
|
193
|
-
|
|
194
|
-
let response;
|
|
195
|
-
try {
|
|
196
|
-
response = await fetch(endpoint, {
|
|
197
|
-
method: 'GET',
|
|
198
|
-
headers: {
|
|
199
|
-
Authorization: `Bearer ${apiToken}`,
|
|
200
|
-
Accept: 'application/json',
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
} catch (err) {
|
|
204
|
-
throw new Error(`Could not reach SRAI API at ${endpoint}: ${err.message}`);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (!response.ok) {
|
|
208
|
-
throw new Error(
|
|
209
|
-
`Project fetch failed (${response.status} ${response.statusText}) at ${endpoint}`,
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
let payload;
|
|
214
|
-
try {
|
|
215
|
-
payload = await response.json();
|
|
216
|
-
} catch {
|
|
217
|
-
throw new Error(`Project fetch returned non-JSON content at ${endpoint}`);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return { payload, endpoint };
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Fetch all project names from SRAI (no filtering).
|
|
225
|
-
*/
|
|
226
|
-
export async function fetchProjectNames(apiUrl, apiToken) {
|
|
227
|
-
const { payload, endpoint } = await fetchProjectsPayload(apiUrl, apiToken);
|
|
228
|
-
const names = extractProjectNames(payload);
|
|
229
|
-
if (names.length === 0) {
|
|
230
|
-
throw new Error(`No project names found in API response from ${endpoint}`);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return names;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Fetch project names that have `is_vibe_review` (or API alias) set true — used for kit init / project switch.
|
|
238
|
-
*/
|
|
239
|
-
export async function fetchVibeReviewProjectNames(apiUrl, apiToken) {
|
|
240
|
-
const { payload, endpoint } = await fetchProjectsPayload(apiUrl, apiToken);
|
|
241
|
-
const entries = extractProjectEntries(payload);
|
|
242
|
-
const names = entries.filter(({ entry }) => isVibeReviewProject(entry)).map(({ name }) => name);
|
|
243
|
-
|
|
244
|
-
if (names.length === 0) {
|
|
245
|
-
throw new Error(
|
|
246
|
-
`No projects with vibe review enabled (is_vibe_review=true) were returned from ${endpoint}. ` +
|
|
247
|
-
'Enable vibe review on at least one SRAI project, or check your API token and URL.',
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return names;
|
|
252
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|