@securityreviewai/securityreview-kit 0.1.24 → 0.1.25
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/package.json +1 -1
- package/src/commands/init.js +17 -5
- package/src/generators/rules/claude.js +5 -1
- package/src/generators/rules/codex.js +5 -1
- package/src/generators/rules/content.js +11 -1
- package/src/generators/rules/cursor.js +5 -1
- package/src/generators/rules/guardrails-init-profile.md +2 -2
- package/src/generators/rules/guardrails-profiler/SKILL.md +3 -2
- package/src/utils/constants.js +7 -0
- package/src/utils/guardrails-profiler-bundle.js +37 -13
- package/src/utils/profiler-agent.js +8 -4
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -3,7 +3,7 @@ import { input, checkbox, confirm, select } from '@inquirer/prompts';
|
|
|
3
3
|
import { TARGETS, TARGET_NAMES } from '../utils/constants.js';
|
|
4
4
|
import { detectTargets } from '../utils/detect.js';
|
|
5
5
|
import { ensureIdeClisForTargets } from '../utils/ide-cli-install.js';
|
|
6
|
-
import {
|
|
6
|
+
import { writeGuardrailsProfilerBundles } from '../utils/guardrails-profiler-bundle.js';
|
|
7
7
|
import {
|
|
8
8
|
pickProfilerAgentTarget,
|
|
9
9
|
runCursorAgentLogin,
|
|
@@ -350,11 +350,23 @@ export async function initCommand(options) {
|
|
|
350
350
|
|
|
351
351
|
if (installRules) {
|
|
352
352
|
try {
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
353
|
+
const bundlePaths = writeGuardrailsProfilerBundles(cwd, {
|
|
354
|
+
projectName: projectNameForSkill,
|
|
355
|
+
targets,
|
|
356
|
+
});
|
|
357
|
+
if (bundlePaths.length === 0) {
|
|
358
|
+
console.log(
|
|
359
|
+
chalk.dim(
|
|
360
|
+
' (Guardrails profiler skill is only written for Cursor, Claude Code, and Codex targets.)',
|
|
361
|
+
),
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
for (const bundlePath of bundlePaths) {
|
|
365
|
+
console.log(chalk.green(` \u2713 Guardrails profiler skill \u2192 ${bundlePath}`));
|
|
366
|
+
results.push({ target: 'kit', type: 'bundle', status: 'ok', path: bundlePath });
|
|
367
|
+
}
|
|
356
368
|
} catch (err) {
|
|
357
|
-
console.log(chalk.red(` \u2717 Guardrails profiler
|
|
369
|
+
console.log(chalk.red(` \u2717 Guardrails profiler skill failed: ${err.message}`));
|
|
358
370
|
results.push({ target: 'kit', type: 'bundle', status: 'error', error: err.message });
|
|
359
371
|
}
|
|
360
372
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
+
import { GUARDRAILS_PROFILER_SKILL_REL_DIR } from '../../utils/constants.js';
|
|
2
3
|
import { upsertSentinelBlock, writeText } from '../../utils/fs-helpers.js';
|
|
3
4
|
import { getRuleContent, getGuardrailsInitProfileContent } from './content.js';
|
|
4
5
|
|
|
@@ -11,7 +12,10 @@ export function generate(cwd, options = {}) {
|
|
|
11
12
|
const action = upsertSentinelBlock(filePath, content);
|
|
12
13
|
|
|
13
14
|
const guardrailsInitPath = join(cwd, '.claude', 'commands', 'guardrails-init-profile.md');
|
|
14
|
-
const guardrailsInitContent = getGuardrailsInitProfileContent(
|
|
15
|
+
const guardrailsInitContent = getGuardrailsInitProfileContent({
|
|
16
|
+
...options,
|
|
17
|
+
guardrailsSkillDir: GUARDRAILS_PROFILER_SKILL_REL_DIR.claude,
|
|
18
|
+
});
|
|
15
19
|
writeText(guardrailsInitPath, guardrailsInitContent);
|
|
16
20
|
|
|
17
21
|
return [
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
+
import { GUARDRAILS_PROFILER_SKILL_REL_DIR } from '../../utils/constants.js';
|
|
2
3
|
import { upsertSentinelBlock, writeText } from '../../utils/fs-helpers.js';
|
|
3
4
|
import { getRuleContent, getGuardrailsInitProfileContent } from './content.js';
|
|
4
5
|
|
|
@@ -11,7 +12,10 @@ export function generate(cwd, options = {}) {
|
|
|
11
12
|
const action = upsertSentinelBlock(filePath, content);
|
|
12
13
|
|
|
13
14
|
const guardrailsInitPath = join(cwd, '.codex', 'commands', 'guardrails-init-profile.md');
|
|
14
|
-
const guardrailsInitContent = getGuardrailsInitProfileContent(
|
|
15
|
+
const guardrailsInitContent = getGuardrailsInitProfileContent({
|
|
16
|
+
...options,
|
|
17
|
+
guardrailsSkillDir: GUARDRAILS_PROFILER_SKILL_REL_DIR.codex,
|
|
18
|
+
});
|
|
15
19
|
writeText(guardrailsInitPath, guardrailsInitContent);
|
|
16
20
|
|
|
17
21
|
return [
|
|
@@ -17,9 +17,19 @@ function readTemplate(templateFileName, options = {}) {
|
|
|
17
17
|
const projectName = sanitizeProjectName(rawProjectName);
|
|
18
18
|
const resolvedProjectName = projectName || '<SRAI_PROJECT_NAME>';
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
let out = template
|
|
21
21
|
.replaceAll('{{SRAI_PROJECT_NAME}}', resolvedProjectName)
|
|
22
22
|
.replaceAll('<SRAI_PROJECT_NAME>', resolvedProjectName);
|
|
23
|
+
|
|
24
|
+
if (out.includes('{{GUARDRAILS_SKILL_DIR}}')) {
|
|
25
|
+
const skillDir =
|
|
26
|
+
typeof options.guardrailsSkillDir === 'string' && options.guardrailsSkillDir.trim()
|
|
27
|
+
? options.guardrailsSkillDir.trim()
|
|
28
|
+
: '.cursor/skills/guardrails-profiler';
|
|
29
|
+
out = out.replaceAll('{{GUARDRAILS_SKILL_DIR}}', skillDir);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return out;
|
|
23
33
|
}
|
|
24
34
|
|
|
25
35
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { writeText } from '../../utils/fs-helpers.js';
|
|
4
|
+
import { GUARDRAILS_PROFILER_SKILL_REL_DIR } from '../../utils/constants.js';
|
|
4
5
|
import {
|
|
5
6
|
getRuleContent,
|
|
6
7
|
getProfileCommandContent,
|
|
@@ -55,7 +56,10 @@ export function generate(cwd, options = {}) {
|
|
|
55
56
|
const ctmSyncWorkflowContent = getCtmSyncWorkflowContent(options);
|
|
56
57
|
const createIdeWorkflowCommandContent = getCreateIdeWorkflowCommandContent(options);
|
|
57
58
|
const profileCommandContent = getProfileCommandContent(options);
|
|
58
|
-
const guardrailsInitProfileCommandContent = getGuardrailsInitProfileContent(
|
|
59
|
+
const guardrailsInitProfileCommandContent = getGuardrailsInitProfileContent({
|
|
60
|
+
...options,
|
|
61
|
+
guardrailsSkillDir: GUARDRAILS_PROFILER_SKILL_REL_DIR.cursor,
|
|
62
|
+
});
|
|
59
63
|
const skillContent = getThreatModellingSkillContent(options);
|
|
60
64
|
|
|
61
65
|
const baseRule = writeCursorRule(
|
|
@@ -5,13 +5,13 @@ description: Run the Security Review Kit guardrails profiler — scan the repo,
|
|
|
5
5
|
|
|
6
6
|
# Guardrails init profile
|
|
7
7
|
|
|
8
|
-
Execute the workflow defined in
|
|
8
|
+
Execute the workflow defined in **`{{GUARDRAILS_SKILL_DIR}}/SKILL.md`** end-to-end in this workspace (this IDE’s copy of the guardrails-profiler skill).
|
|
9
9
|
|
|
10
10
|
Configured SRAI project name: `<SRAI_PROJECT_NAME>`
|
|
11
11
|
|
|
12
12
|
**You must:**
|
|
13
13
|
|
|
14
|
-
1. Read
|
|
14
|
+
1. Read `{{GUARDRAILS_SKILL_DIR}}/SKILL.md` and follow every step (use the signal registry at `{{GUARDRAILS_SKILL_DIR}}/references/signal-registry.json`).
|
|
15
15
|
2. Write `.guardrails/profile.json` and **`profile.json`** at the project root as specified.
|
|
16
16
|
3. Call **`update_vibe_profile`** and **`write_default_pack`** on `security-review-mcp` after resolving `project_id` for `<SRAI_PROJECT_NAME>`.
|
|
17
17
|
|
|
@@ -11,7 +11,8 @@ Configured SRAI project name: `<SRAI_PROJECT_NAME>`
|
|
|
11
11
|
|
|
12
12
|
## Canonical paths
|
|
13
13
|
|
|
14
|
-
- **
|
|
14
|
+
- **This skill & signal registry (read-only):** `<GUARDRAILS_SKILL_DIR>/` — e.g. `.cursor/skills/guardrails-profiler`, `.claude/skills/guardrails-profiler`, or `.codex/skills/guardrails-profiler` depending on where this file was installed.
|
|
15
|
+
- **Signal registry file:** `<GUARDRAILS_SKILL_DIR>/references/signal-registry.json`
|
|
15
16
|
- **Local guardrails file:** `.guardrails/profile.json`
|
|
16
17
|
- **Combined manifest (project root):** `profile.json` — includes guardrails profile, vibe profile fields for MCP, and default pack payload
|
|
17
18
|
|
|
@@ -38,7 +39,7 @@ The project root is the current working directory. Confirm with markers such as
|
|
|
38
39
|
|
|
39
40
|
### Step 2: Read the Signal Registry
|
|
40
41
|
|
|
41
|
-
Read
|
|
42
|
+
Read `<GUARDRAILS_SKILL_DIR>/references/signal-registry.json` (the copy next to this `SKILL.md`). Use its categories (`universal`, `languages`, `frameworks`, `auth_identity`, `ai_agent`, `infrastructure`, `ci_cd`, `cloud_compute`, `databases`, `messaging`, `api_protocols`, etc.) to map detected signals to guardrail pack IDs.
|
|
42
43
|
|
|
43
44
|
### Step 3: Scan for Signals
|
|
44
45
|
|
package/src/utils/constants.js
CHANGED
|
@@ -59,5 +59,12 @@ export const TARGETS = {
|
|
|
59
59
|
|
|
60
60
|
export const TARGET_NAMES = Object.keys(TARGETS);
|
|
61
61
|
|
|
62
|
+
/** Relative workspace dirs for the guardrails-profiler skill (per IDE / CLI). */
|
|
63
|
+
export const GUARDRAILS_PROFILER_SKILL_REL_DIR = {
|
|
64
|
+
cursor: '.cursor/skills/guardrails-profiler',
|
|
65
|
+
claude: '.claude/skills/guardrails-profiler',
|
|
66
|
+
codex: '.codex/skills/guardrails-profiler',
|
|
67
|
+
};
|
|
68
|
+
|
|
62
69
|
export const SENTINEL_START = '<!-- securityreview-kit:start -->';
|
|
63
70
|
export const SENTINEL_END = '<!-- securityreview-kit:end -->';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { copyFileSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { GUARDRAILS_PROFILER_SKILL_REL_DIR } from './constants.js';
|
|
4
5
|
import { ensureDir, writeText } from './fs-helpers.js';
|
|
5
6
|
|
|
6
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -14,20 +15,43 @@ function injectProjectName(content, projectName) {
|
|
|
14
15
|
return content.replaceAll('{{SRAI_PROJECT_NAME}}', resolved).replaceAll('<SRAI_PROJECT_NAME>', resolved);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* Writes `.securityreview-kit/guardrails-profiler/` (SKILL + signal registry) into the target workspace.
|
|
21
|
-
*/
|
|
22
|
-
export function writeGuardrailsProfilerBundle(cwd, options = {}) {
|
|
23
|
-
const destBase = join(cwd, '.securityreview-kit', 'guardrails-profiler');
|
|
24
|
-
const destRefs = join(destBase, 'references');
|
|
25
|
-
ensureDir(destRefs);
|
|
18
|
+
function injectSkillDir(content, skillDirRel) {
|
|
19
|
+
return content.replaceAll('<GUARDRAILS_SKILL_DIR>', skillDirRel);
|
|
20
|
+
}
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
function injectSkillTemplate(content, projectName, skillDirRel) {
|
|
23
|
+
return injectSkillDir(injectProjectName(content, projectName), skillDirRel);
|
|
24
|
+
}
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
const BUNDLE_ROOT = join(__dirname, '..', 'generators', 'rules', 'guardrails-profiler');
|
|
31
27
|
|
|
32
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Writes `guardrails-profiler` (SKILL.md + references/signal-registry.json) under each selected
|
|
30
|
+
* IDE skills directory (e.g. `.cursor/skills/guardrails-profiler`, `.claude/skills/...`).
|
|
31
|
+
*
|
|
32
|
+
* @returns {string[]} Absolute paths to each skill root written */
|
|
33
|
+
export function writeGuardrailsProfilerBundles(cwd, options = {}) {
|
|
34
|
+
const targets = Array.isArray(options.targets) ? options.targets : [];
|
|
35
|
+
const written = [];
|
|
36
|
+
|
|
37
|
+
for (const target of targets) {
|
|
38
|
+
const rel = GUARDRAILS_PROFILER_SKILL_REL_DIR[target];
|
|
39
|
+
if (!rel) continue;
|
|
40
|
+
|
|
41
|
+
const destBase = join(cwd, rel);
|
|
42
|
+
const destRefs = join(destBase, 'references');
|
|
43
|
+
ensureDir(destRefs);
|
|
44
|
+
|
|
45
|
+
const skillTemplate = readFileSync(join(BUNDLE_ROOT, 'SKILL.md'), 'utf-8');
|
|
46
|
+
writeText(join(destBase, 'SKILL.md'), injectSkillTemplate(skillTemplate, options.projectName, rel));
|
|
47
|
+
|
|
48
|
+
copyFileSync(
|
|
49
|
+
join(BUNDLE_ROOT, 'references', 'signal-registry.json'),
|
|
50
|
+
join(destRefs, 'signal-registry.json'),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
written.push(destBase);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return written;
|
|
33
57
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { GUARDRAILS_PROFILER_SKILL_REL_DIR } from './constants.js';
|
|
2
3
|
|
|
3
4
|
const PREFERRED_ORDER = ['cursor', 'claude', 'codex'];
|
|
4
5
|
|
|
@@ -7,13 +8,16 @@ function commandOk(cmd, args = ['--version']) {
|
|
|
7
8
|
return r.status === 0;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export function buildProfilerAgentPrompt(projectName) {
|
|
11
|
+
export function buildProfilerAgentPrompt(projectName, agentTarget = 'cursor') {
|
|
11
12
|
const p = String(projectName || '').trim() || '<SRAI_PROJECT_NAME>';
|
|
13
|
+
const skillRoot =
|
|
14
|
+
GUARDRAILS_PROFILER_SKILL_REL_DIR[agentTarget] ||
|
|
15
|
+
GUARDRAILS_PROFILER_SKILL_REL_DIR.cursor;
|
|
12
16
|
return [
|
|
13
17
|
'Security Review Kit: run guardrails initialization profiling for this workspace.',
|
|
14
18
|
`SRAI project name: "${p}".`,
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
`Open and follow every step in ${skillRoot}/SKILL.md.`,
|
|
20
|
+
`Use ${skillRoot}/references/signal-registry.json as the signal registry.`,
|
|
17
21
|
'Write .guardrails/profile.json and profile.json at the repository root as defined in the skill.',
|
|
18
22
|
`Resolve project_id with find_project_by_name for "${p}", then call update_vibe_profile and write_default_pack via security-review-mcp.`,
|
|
19
23
|
'Do not invent stack details, compliance, or user groups; ground everything in the repository.',
|
|
@@ -50,7 +54,7 @@ export function pickProfilerAgentTarget(targets) {
|
|
|
50
54
|
* if you need an interactive trust/login/MCP flow in the same terminal.
|
|
51
55
|
*/
|
|
52
56
|
export function runProfilerAgent(cwd, { target, projectName, cursorTrust = true }) {
|
|
53
|
-
const prompt = buildProfilerAgentPrompt(projectName);
|
|
57
|
+
const prompt = buildProfilerAgentPrompt(projectName, target);
|
|
54
58
|
const opts = { cwd, stdio: 'inherit', env: { ...process.env } };
|
|
55
59
|
|
|
56
60
|
if (target === 'cursor') {
|