@securityreviewai/securityreview-kit 0.1.30 → 0.1.32
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 +5 -5
- package/src/generators/rules/claude.js +5 -2
- package/src/generators/rules/codex.js +5 -2
- package/src/generators/rules/content.js +23 -7
- package/src/generators/rules/content.md +10 -6
- package/src/generators/rules/ctm_sync.md +8 -6
- package/src/generators/rules/ctm_sync_rule.md +1 -1
- package/src/generators/rules/cursor.js +17 -9
- package/src/generators/rules/guardrails-selection/SKILL.md +187 -0
- package/src/generators/rules/guardrails-selection/references/category-threat-map.md +232 -0
- package/src/generators/rules/guardrails_rule.md +21 -13
- package/src/generators/rules/hooks.json +2 -2
- package/src/generators/rules/skill.md +5 -5
- package/src/utils/constants.js +7 -0
- package/src/utils/guardrails-profiler-bundle.js +45 -18
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 { writeGuardrailsSkillBundles } from '../utils/guardrails-profiler-bundle.js';
|
|
7
7
|
import {
|
|
8
8
|
pickProfilerAgentTarget,
|
|
9
9
|
runCursorAgentLogin,
|
|
@@ -352,23 +352,23 @@ export async function initCommand(options) {
|
|
|
352
352
|
|
|
353
353
|
if (installRules) {
|
|
354
354
|
try {
|
|
355
|
-
const bundlePaths =
|
|
355
|
+
const bundlePaths = writeGuardrailsSkillBundles(cwd, {
|
|
356
356
|
projectName: projectNameForSkill,
|
|
357
357
|
targets,
|
|
358
358
|
});
|
|
359
359
|
if (bundlePaths.length === 0) {
|
|
360
360
|
console.log(
|
|
361
361
|
chalk.dim(
|
|
362
|
-
' (Guardrails
|
|
362
|
+
' (Guardrails skills are only written for Cursor, Claude Code, and Codex targets.)',
|
|
363
363
|
),
|
|
364
364
|
);
|
|
365
365
|
}
|
|
366
366
|
for (const bundlePath of bundlePaths) {
|
|
367
|
-
console.log(chalk.green(` \u2713 Guardrails
|
|
367
|
+
console.log(chalk.green(` \u2713 Guardrails skill bundle \u2192 ${bundlePath}`));
|
|
368
368
|
results.push({ target: 'kit', type: 'bundle', status: 'ok', path: bundlePath });
|
|
369
369
|
}
|
|
370
370
|
} catch (err) {
|
|
371
|
-
console.log(chalk.red(` \u2717 Guardrails
|
|
371
|
+
console.log(chalk.red(` \u2717 Guardrails skill bundle failed: ${err.message}`));
|
|
372
372
|
results.push({ target: 'kit', type: 'bundle', status: 'error', error: err.message });
|
|
373
373
|
}
|
|
374
374
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { GUARDRAILS_PROFILER_SKILL_REL_DIR } from '../../utils/constants.js';
|
|
2
|
+
import { GUARDRAILS_PROFILER_SKILL_REL_DIR, GUARDRAILS_SELECTION_SKILL_REL_DIR } from '../../utils/constants.js';
|
|
3
3
|
import { upsertSentinelBlock, writeText } from '../../utils/fs-helpers.js';
|
|
4
4
|
import { getRuleContent, getGuardrailsInitProfileContent } from './content.js';
|
|
5
5
|
|
|
@@ -8,7 +8,10 @@ import { getRuleContent, getGuardrailsInitProfileContent } from './content.js';
|
|
|
8
8
|
*/
|
|
9
9
|
export function generate(cwd, options = {}) {
|
|
10
10
|
const filePath = join(cwd, 'CLAUDE.md');
|
|
11
|
-
const content = getRuleContent(
|
|
11
|
+
const content = getRuleContent({
|
|
12
|
+
...options,
|
|
13
|
+
guardrailsSelectionSkillDir: GUARDRAILS_SELECTION_SKILL_REL_DIR.claude,
|
|
14
|
+
});
|
|
12
15
|
const action = upsertSentinelBlock(filePath, content);
|
|
13
16
|
|
|
14
17
|
const guardrailsInitPath = join(cwd, '.claude', 'commands', 'guardrails-init-profile.md');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { GUARDRAILS_PROFILER_SKILL_REL_DIR } from '../../utils/constants.js';
|
|
2
|
+
import { GUARDRAILS_PROFILER_SKILL_REL_DIR, GUARDRAILS_SELECTION_SKILL_REL_DIR } from '../../utils/constants.js';
|
|
3
3
|
import { upsertSentinelBlock, writeText } from '../../utils/fs-helpers.js';
|
|
4
4
|
import { getRuleContent, getGuardrailsInitProfileContent } from './content.js';
|
|
5
5
|
|
|
@@ -8,7 +8,10 @@ import { getRuleContent, getGuardrailsInitProfileContent } from './content.js';
|
|
|
8
8
|
*/
|
|
9
9
|
export function generate(cwd, options = {}) {
|
|
10
10
|
const filePath = join(cwd, 'AGENTS.md');
|
|
11
|
-
const content = getRuleContent(
|
|
11
|
+
const content = getRuleContent({
|
|
12
|
+
...options,
|
|
13
|
+
guardrailsSelectionSkillDir: GUARDRAILS_SELECTION_SKILL_REL_DIR.codex,
|
|
14
|
+
});
|
|
12
15
|
const action = upsertSentinelBlock(filePath, content);
|
|
13
16
|
|
|
14
17
|
const guardrailsInitPath = join(cwd, '.codex', 'commands', 'guardrails-init-profile.md');
|
|
@@ -8,6 +8,17 @@ function sanitizeProjectName(value) {
|
|
|
8
8
|
return value.replace(/[\r\n`]/g, ' ').trim();
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
function injectPathPlaceholder(content, placeholder, fallbackPath, configuredPath) {
|
|
12
|
+
if (!content.includes(placeholder)) {
|
|
13
|
+
return content;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const resolvedPath =
|
|
17
|
+
typeof configuredPath === 'string' && configuredPath.trim() ? configuredPath.trim() : fallbackPath;
|
|
18
|
+
|
|
19
|
+
return content.replaceAll(placeholder, resolvedPath);
|
|
20
|
+
}
|
|
21
|
+
|
|
11
22
|
/**
|
|
12
23
|
* Reads a markdown template and injects the configured project name placeholder.
|
|
13
24
|
*/
|
|
@@ -21,13 +32,18 @@ function readTemplate(templateFileName, options = {}) {
|
|
|
21
32
|
.replaceAll('{{SRAI_PROJECT_NAME}}', resolvedProjectName)
|
|
22
33
|
.replaceAll('<SRAI_PROJECT_NAME>', resolvedProjectName);
|
|
23
34
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
out = injectPathPlaceholder(
|
|
36
|
+
out,
|
|
37
|
+
'{{GUARDRAILS_SKILL_DIR}}',
|
|
38
|
+
'.cursor/skills/guardrails-profiler',
|
|
39
|
+
options.guardrailsSkillDir,
|
|
40
|
+
);
|
|
41
|
+
out = injectPathPlaceholder(
|
|
42
|
+
out,
|
|
43
|
+
'{{GUARDRAILS_SELECTION_SKILL_DIR}}',
|
|
44
|
+
'.cursor/skills/guardrails-selection',
|
|
45
|
+
options.guardrailsSelectionSkillDir,
|
|
46
|
+
);
|
|
31
47
|
|
|
32
48
|
return out;
|
|
33
49
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
**THIS RULE IS NON-NEGOTIABLE AND TRIGGERED BEFORE YOU EVEN START THINKING.** You MUST think security-first and perform on-the-go threat modeling using the PWNISMS framework BEFORE and DURING every task whether it's TODO or PLAN or codegen. This is not optional. This is not deferred to a later review. Every line of code you write must be informed by threat awareness.
|
|
4
4
|
|
|
5
5
|
> **Skill Reference:** Read and apply `.cursor/skills/threat-modelling/SKILL.md` for the full PWNISMS framework — the 7 threat categories, code generation rules, and post-generation checklist. You MUST walk through all 7 categories for every code task that seems security relevant.
|
|
6
|
+
>
|
|
7
|
+
> **Guardrail Selection Skill:** In IDEs that support workspace skills, read and apply `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md` when fetching guardrails. In other IDEs, follow that same workflow explicitly: analyze the task, shortlist relevant guardrails, and hydrate the exact shortlist with `get_guardrail_by_id`.
|
|
6
8
|
|
|
7
9
|
### When NOT to apply
|
|
8
10
|
|
|
@@ -26,7 +28,7 @@ If in doubt, do a quick mental scan: *does this change touch auth, input handlin
|
|
|
26
28
|
This workflow enforces security through two complementary pillars:
|
|
27
29
|
|
|
28
30
|
1. **Secure by Design (PWNISMS)** — Universal threat-category analysis applied to every security-relevant task. Identifies risks and drives architectural/design-level mitigations.
|
|
29
|
-
2. **Secure by Code (Vibe Guardrails)** — Project-specific coding dos and don'ts fetched from SRAI via `get_guardrails`. Enforces concrete implementation rules derived from prior threat reviews, compliance requirements, and team decisions.
|
|
31
|
+
2. **Secure by Code (Vibe Guardrails)** — Project-specific coding dos and don'ts fetched from SRAI via `get_guardrails`, shortlisted intentionally, and then hydrated via `get_guardrail_by_id`. Enforces concrete implementation rules derived from prior threat reviews, compliance requirements, and team decisions.
|
|
30
32
|
|
|
31
33
|
Both pillars are **mandatory** for any code-generation task with a security surface. PWNISMS catches broad threats; guardrails enforce project-specific coding standards.
|
|
32
34
|
|
|
@@ -78,10 +80,12 @@ Rather than fetching the entire profile at once, walk through each reasoning blo
|
|
|
78
80
|
- For each requirement relevant to the task, call `get_profile_compliance_requirement` for full details.
|
|
79
81
|
- Map these to security objectives and ensure your implementation meets them.
|
|
80
82
|
|
|
81
|
-
10. **Fetch Vibe Guardrails** —
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
83
|
+
10. **Fetch Vibe Guardrails** — Follow `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md`.
|
|
84
|
+
- Call `get_guardrails` with `project_id` to load the broad catalog.
|
|
85
|
+
- Analyze the task and infer the relevant security categories and likely threats first.
|
|
86
|
+
- Shortlist only the guardrails relevant to the task and fetch the exact shortlist with `get_guardrail_by_id`.
|
|
87
|
+
- Separate the shortlisted guardrails into `must` (mandatory requirements) and `must_not` (hard prohibitions).
|
|
88
|
+
- Keep the shortlisted exact guardrails in context for the entire code-generation task and for later `ctm_sync`.
|
|
85
89
|
- Cross-reference with PWNISMS findings: if a threat has no corresponding guardrail, flag it as a candidate for a new guardrail.
|
|
86
90
|
|
|
87
91
|
11. **Check for existing reviews** — Call `list_reviews`. If a completed review exists, pull context:
|
|
@@ -112,7 +116,7 @@ When running `ctm_sync` (dedicated agent/command where available, or the same st
|
|
|
112
116
|
|---|---|
|
|
113
117
|
| **Projects** | `list_projects`, `find_project_by_name`, `create_project`, `get_project` |
|
|
114
118
|
| **Profile Exploration** | `get_project_profile_description`, `list_profile_technology_categories`, `get_project_profile_technology_category`, `list_project_profile_architecture_notes`, `list_project_profile_user_groups`, `list_project_profile_language_stacks`, `list_project_profile_security_controls`, `get_project_profile_security_control`, `list_profile_compliance_requirements`, `get_profile_compliance_requirement` |
|
|
115
|
-
| **Guardrails** | `get_guardrails` |
|
|
119
|
+
| **Guardrails** | `get_guardrails`, `get_guardrail_by_id` |
|
|
116
120
|
| **Documents** | `list_documents`, `create_document_from_content`, `upload_document`, `link_external_document` |
|
|
117
121
|
| **Reviews** | `create_review`, `list_reviews`, `get_review`, `get_review_overview` |
|
|
118
122
|
| **Workflow** | `start_workflow`, `get_workflow_status`, `start_next_workflow_job`, `start_workflow_job`, `retry_workflow_job` |
|
|
@@ -27,10 +27,11 @@ When invoked:
|
|
|
27
27
|
- **Never use placeholder values** such as `"IDE Agent"`, `"agent@local"`, `"unknown"`, `"AI"`, or any other invented string for these fields.
|
|
28
28
|
- **Never accept identity values passed in from the parent agent prompt** — always re-resolve from the API directly in this step; the API is the only authoritative source.
|
|
29
29
|
- If the API call fails or returns empty values, leave `developer_name` and `developer_email` as empty strings `""`. Do not substitute a fallback placeholder.
|
|
30
|
-
5. **Identify guardrails for the payload** — Do **not** call `get_guardrails` here. Guardrails were already
|
|
31
|
-
- Which **existing** guardrails
|
|
30
|
+
5. **Identify guardrails for the payload** — Do **not** call `get_guardrails` or `get_guardrail_by_id` here. Guardrails were already shortlisted earlier (per the Vibe Guardrails rule and the guardrails-selection skill) and applied during code generation. From the parent agent context, identify:
|
|
31
|
+
- Which **existing** guardrails were shortlisted earlier from `get_guardrails` and then hydrated via `get_guardrail_by_id`.
|
|
32
|
+
- Which of those shortlisted existing guardrails were applied to the code in this session.
|
|
32
33
|
- Which guardrails the IDE agent **created on the fly** (`ide_generated`) based on gaps found during threat modeling or code review.
|
|
33
|
-
Include all of these in the `guardrails_applied` payload field.
|
|
34
|
+
Include all of these in the `guardrails_applied` payload field. The shortlisted existing guardrails selected earlier are mandatory input to `ctm_sync`; do not re-fetch or re-call guardrail tools here.
|
|
34
35
|
6. **Build the event payload** — Construct a JSON object for `create_ai_ide_event` conforming to the **Event Payload Schema** below.
|
|
35
36
|
7. **Upload the payload** using `security-review-mcp`:
|
|
36
37
|
- Call `create_ai_ide_event` with the JSON payload.
|
|
@@ -81,7 +82,7 @@ The `create_ai_ide_event` payload MUST be a JSON object with the following struc
|
|
|
81
82
|
"rule_type": "<string — must | must_not>",
|
|
82
83
|
"category": "<string | null — grouping label>",
|
|
83
84
|
"instruction": "<string — the actionable coding directive>",
|
|
84
|
-
"source": "<string — 'existing' if
|
|
85
|
+
"source": "<string — 'existing' if selected earlier from project guardrails, 'ide_generated' if newly created by the IDE agent>",
|
|
85
86
|
"satisfied": "<boolean — true if the guardrail was fully satisfied, false if partially or not satisfied>",
|
|
86
87
|
"notes": "<string — optional: how it was applied, why it could not be fully satisfied, or rationale for a new guardrail>"
|
|
87
88
|
}
|
|
@@ -108,7 +109,7 @@ The `create_ai_ide_event` payload MUST be a JSON object with the following struc
|
|
|
108
109
|
| `threats_mitigated` | Yes | Array, may be empty `[]` if no threats were identified. Each entry must include a `code_snippet` grounded in actual source code |
|
|
109
110
|
| `best_practises_achieved` | Yes | Array of strings, may be empty `[]` |
|
|
110
111
|
| `secure_code_snippets` | Yes | Array, may be empty `[]` |
|
|
111
|
-
| `guardrails_applied` | Yes | Array of all guardrails enforced during this session — both existing ones from
|
|
112
|
+
| `guardrails_applied` | Yes | Array of all guardrails enforced during this session — both existing ones shortlisted earlier from project guardrails and new ones the IDE agent created. Use `source` to distinguish origin. Empty `[]` if none |
|
|
112
113
|
| `owasp_top_10_2025_mappings` | Yes | Array of OWASP Top 10 2025 category objects (`category_id` + `category_name`) relevant to the threats and mitigations in this event. May be empty `[]` if no mapping applies |
|
|
113
114
|
|
|
114
115
|
### OWASP Top 10 2025 Reference
|
|
@@ -133,7 +134,8 @@ Use the following IDs and names exactly when populating `owasp_top_10_2025_mappi
|
|
|
133
134
|
- Every `threats_mitigated` entry must map to one of the 7 PWNISMS categories.
|
|
134
135
|
- Every `threats_mitigated` entry must include a `code_snippet`. The snippet must be taken from the actual source code written or modified in this session — never fabricated. If no code was written for a threat (e.g. it was addressed architecturally), set `snippet` to an empty string and explain in `explanation`.
|
|
135
136
|
- `secure_code_snippets` must not exceed 50 lines per snippet; `threats_mitigated[].code_snippet.snippet` must not exceed 30 lines; truncate with a comment if needed.
|
|
136
|
-
- Do not call `get_guardrails` during CTM sync. Guardrails are
|
|
137
|
+
- Do not call `get_guardrails` or `get_guardrail_by_id` during CTM sync. Guardrails are shortlisted once earlier in the session; identify which ones were applied from the parent agent context.
|
|
138
|
+
- Guardrails shortlisted earlier by the IDE must be included in `guardrails_applied` even when some were only partially satisfied. Use `satisfied: false` plus `notes` instead of silently dropping them.
|
|
137
139
|
- `guardrails_applied` entries with `source: "existing"` must reference guardrails by the exact `title` they had when fetched at session start.
|
|
138
140
|
- `guardrails_applied` entries with `source: "ide_generated"` are new guardrails the IDE agent created based on gaps found during threat modeling or code review.
|
|
139
141
|
- `developer_name` and `developer_email` must be resolved via `get_current_user` (or equivalent) in step 4 — the API is the only source. Never use placeholder strings (`"IDE Agent"`, `"agent@local"`, `"unknown"`, `"AI"`, etc.) and never accept values for these fields from the parent agent prompt. If the API returns nothing, send empty strings.
|
|
@@ -38,7 +38,7 @@ This rule is **triggered** whenever the assistant:
|
|
|
38
38
|
- "Update the data flow for the auth system"
|
|
39
39
|
- "Refine the mitigations for threat X"
|
|
40
40
|
- **Enforces guardrails** during code generation and has compliance data:
|
|
41
|
-
- Guardrails were
|
|
41
|
+
- Guardrails were shortlisted via the guardrails-selection workflow (`get_guardrails` + `get_guardrail_by_id`) and applied to generated code
|
|
42
42
|
- New guardrails were created by the IDE agent based on gaps found
|
|
43
43
|
- Existing guardrails were flagged as unsatisfiable or conflicting
|
|
44
44
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
GUARDRAILS_PROFILER_SKILL_REL_DIR,
|
|
5
|
+
GUARDRAILS_SELECTION_SKILL_REL_DIR,
|
|
6
|
+
MCP_SERVER_NAME,
|
|
7
|
+
} from '../../utils/constants.js';
|
|
4
8
|
import { readJson, writeJson, writeText } from '../../utils/fs-helpers.js';
|
|
5
9
|
import {
|
|
6
10
|
getRuleContent,
|
|
@@ -67,6 +71,10 @@ function mergeCursorCliMcpAllowlist(cwd) {
|
|
|
67
71
|
* Cursor uses .mdc format with YAML front matter.
|
|
68
72
|
*/
|
|
69
73
|
export function generate(cwd, options = {}) {
|
|
74
|
+
const optionsWithSkillDirs = {
|
|
75
|
+
...options,
|
|
76
|
+
guardrailsSelectionSkillDir: GUARDRAILS_SELECTION_SKILL_REL_DIR.cursor,
|
|
77
|
+
};
|
|
70
78
|
const baseRulePath = join(cwd, '.cursor', 'rules', 'srai-security-review.mdc');
|
|
71
79
|
const ctmSyncTriggerRulePath = join(cwd, '.cursor', 'rules', 'ctm_sync_rule.mdc');
|
|
72
80
|
const guardrailsRulePath = join(cwd, '.cursor', 'rules', 'guardrails_rule.mdc');
|
|
@@ -78,17 +86,17 @@ export function generate(cwd, options = {}) {
|
|
|
78
86
|
const skillPath = join(cwd, '.cursor', 'skills', 'threat-modelling', 'SKILL.md');
|
|
79
87
|
const hooksPath = join(cwd, '.cursor', 'hooks.json');
|
|
80
88
|
|
|
81
|
-
const baseRuleContent = getRuleContent(
|
|
82
|
-
const ctmSyncTriggerRuleContent = getCtmSyncTriggerRuleContent(
|
|
83
|
-
const guardrailsRuleContent = getGuardrailsRuleContent(
|
|
84
|
-
const ctmSyncWorkflowContent = getCtmSyncWorkflowContent(
|
|
85
|
-
const createIdeWorkflowCommandContent = getCreateIdeWorkflowCommandContent(
|
|
86
|
-
const profileCommandContent = getProfileCommandContent(
|
|
89
|
+
const baseRuleContent = getRuleContent(optionsWithSkillDirs);
|
|
90
|
+
const ctmSyncTriggerRuleContent = getCtmSyncTriggerRuleContent(optionsWithSkillDirs);
|
|
91
|
+
const guardrailsRuleContent = getGuardrailsRuleContent(optionsWithSkillDirs);
|
|
92
|
+
const ctmSyncWorkflowContent = getCtmSyncWorkflowContent(optionsWithSkillDirs);
|
|
93
|
+
const createIdeWorkflowCommandContent = getCreateIdeWorkflowCommandContent(optionsWithSkillDirs);
|
|
94
|
+
const profileCommandContent = getProfileCommandContent(optionsWithSkillDirs);
|
|
87
95
|
const guardrailsInitProfileCommandContent = getGuardrailsInitProfileContent({
|
|
88
|
-
...
|
|
96
|
+
...optionsWithSkillDirs,
|
|
89
97
|
guardrailsSkillDir: GUARDRAILS_PROFILER_SKILL_REL_DIR.cursor,
|
|
90
98
|
});
|
|
91
|
-
const skillContent = getThreatModellingSkillContent(
|
|
99
|
+
const skillContent = getThreatModellingSkillContent(optionsWithSkillDirs);
|
|
92
100
|
|
|
93
101
|
const baseRule = writeCursorRule(
|
|
94
102
|
baseRulePath,
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: guardrails-selection
|
|
3
|
+
description: Analyze the developer request, infer the security categories and likely threats involved, shortlist the most relevant project guardrails, then hydrate the exact guardrails with get_guardrail_by_id before implementation. Use for every security-relevant code task before code is written and preserve the shortlist for CTM sync.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Guardrails Selection
|
|
7
|
+
|
|
8
|
+
Configured SRAI project name: `<SRAI_PROJECT_NAME>`
|
|
9
|
+
|
|
10
|
+
Use this skill whenever code will be created or modified and the task has any security surface.
|
|
11
|
+
|
|
12
|
+
This skill exists to stop the IDE from treating the full `get_guardrails` result as an unstructured blob. The workflow is:
|
|
13
|
+
|
|
14
|
+
1. Understand the request deeply.
|
|
15
|
+
2. Infer which security categories are in play.
|
|
16
|
+
3. Predict the threats that might occur for this exact task.
|
|
17
|
+
4. Shortlist only the guardrails that mitigate those threats.
|
|
18
|
+
5. Fetch the exact shortlisted guardrails with `get_guardrail_by_id`.
|
|
19
|
+
6. Carry that same shortlist forward into implementation and `ctm_sync`.
|
|
20
|
+
|
|
21
|
+
Do not skip the analysis step. Do not rely on title-matching alone. Do not dump every guardrail into the final answer.
|
|
22
|
+
|
|
23
|
+
## Inputs You Must Analyze First
|
|
24
|
+
|
|
25
|
+
Before calling `get_guardrails`, extract the actual development intent from the prompt and surrounding code:
|
|
26
|
+
|
|
27
|
+
- What is being built, changed, fixed, or refactored?
|
|
28
|
+
- Which components are affected: API, UI, background jobs, auth flow, webhook, file upload, admin tooling, AI agent flow, infra code, data pipeline?
|
|
29
|
+
- Which trust boundaries are crossed?
|
|
30
|
+
- Which sensitive assets are touched: tokens, credentials, sessions, PII, tenancy boundaries, audit logs, secrets, internal APIs, signed URLs, payment state, workflow approvals?
|
|
31
|
+
- Which technologies and patterns are involved in the existing code?
|
|
32
|
+
- What abuse cases are plausible if this change is implemented poorly?
|
|
33
|
+
|
|
34
|
+
You are not only selecting guardrails for the obvious functionality. You are selecting guardrails for the threats that might materialize around that functionality.
|
|
35
|
+
|
|
36
|
+
## Category Inference Workflow
|
|
37
|
+
|
|
38
|
+
Derive a category set for the task before shortlisting guardrails. Common categories include:
|
|
39
|
+
|
|
40
|
+
- `authentication`
|
|
41
|
+
- `authorization`
|
|
42
|
+
- `session_management`
|
|
43
|
+
- `input_validation`
|
|
44
|
+
- `output_encoding`
|
|
45
|
+
- `secrets`
|
|
46
|
+
- `cryptography`
|
|
47
|
+
- `logging`
|
|
48
|
+
- `monitoring`
|
|
49
|
+
- `file_uploads`
|
|
50
|
+
- `deserialization`
|
|
51
|
+
- `data_access`
|
|
52
|
+
- `rate_limiting`
|
|
53
|
+
- `network`
|
|
54
|
+
- `client_side`
|
|
55
|
+
- `business_logic`
|
|
56
|
+
- `tenant_isolation`
|
|
57
|
+
- `admin_workflows`
|
|
58
|
+
|
|
59
|
+
Use both the user request and the codebase patterns to infer the category set. A task can involve multiple categories even if the prompt mentions only one feature.
|
|
60
|
+
|
|
61
|
+
Examples:
|
|
62
|
+
|
|
63
|
+
- “Add magic-link login” likely involves `authentication`, `session_management`, `cryptography`, `logging`, `rate_limiting`, and `client_side`.
|
|
64
|
+
- “Add org admin API to update member roles” likely involves `authorization`, `tenant_isolation`, `logging`, `business_logic`, and `data_access`.
|
|
65
|
+
- “Add CSV import” likely involves `input_validation`, `file_uploads`, `data_access`, `deserialization`, `logging`, and denial-of-service protections.
|
|
66
|
+
- “Add client-side token refresh” likely involves `authentication`, `session_management`, `client_side`, `logging`, and `cryptography`.
|
|
67
|
+
|
|
68
|
+
## Threat Mapping Requirement
|
|
69
|
+
|
|
70
|
+
After identifying categories, infer the threat families that might occur. Use the reference file at `{{GUARDRAILS_SELECTION_SKILL_DIR}}/references/category-threat-map.md` every time you need to reason about category-to-threat mapping.
|
|
71
|
+
|
|
72
|
+
Your goal is not to enumerate every possible weakness. Your goal is to pick the threats that should influence guardrail selection for this task.
|
|
73
|
+
|
|
74
|
+
At minimum, consider whether the task can create:
|
|
75
|
+
|
|
76
|
+
- authentication bypass
|
|
77
|
+
- authorization bypass
|
|
78
|
+
- privilege escalation
|
|
79
|
+
- information disclosure
|
|
80
|
+
- repudiation gaps
|
|
81
|
+
- denial of service
|
|
82
|
+
- unsafe client-side trust
|
|
83
|
+
- insecure logging or audit gaps
|
|
84
|
+
- injection-triggered security failures
|
|
85
|
+
- serialization-triggered security failures
|
|
86
|
+
- business-logic-triggered bypasses
|
|
87
|
+
|
|
88
|
+
The shortlist should be threat-led, not catalog-led.
|
|
89
|
+
|
|
90
|
+
## Guardrail Selection Procedure
|
|
91
|
+
|
|
92
|
+
### Step 1: Resolve the project and load the catalog
|
|
93
|
+
|
|
94
|
+
1. Call `find_project_by_name` with `name="<SRAI_PROJECT_NAME>"` to obtain `project_id`.
|
|
95
|
+
2. Call `get_guardrails` with `project_id`.
|
|
96
|
+
|
|
97
|
+
Treat `get_guardrails` as the broad catalog. Do not treat it as the final set of instructions.
|
|
98
|
+
|
|
99
|
+
Assume each returned guardrail includes the fields needed for selection, including a stable identifier for follow-up retrieval, plus:
|
|
100
|
+
|
|
101
|
+
- `title`
|
|
102
|
+
- `rule_type`
|
|
103
|
+
- `category`
|
|
104
|
+
- `instruction`
|
|
105
|
+
|
|
106
|
+
If an identifier is absent, fall back to the best available stable reference exposed by the tool, but prefer the real guardrail id whenever available.
|
|
107
|
+
|
|
108
|
+
### Step 2: Build a shortlist
|
|
109
|
+
|
|
110
|
+
Shortlist guardrails using all of the following:
|
|
111
|
+
|
|
112
|
+
- direct category match with the task
|
|
113
|
+
- mitigation value against the likely threats you inferred
|
|
114
|
+
- relevance to the technologies and code paths being touched
|
|
115
|
+
- support for adjacent controls that prevent bypass chains
|
|
116
|
+
- duplication removal
|
|
117
|
+
|
|
118
|
+
Do not select a guardrail only because it sounds generally useful. Select it because it materially constrains the risky part of the current task.
|
|
119
|
+
|
|
120
|
+
Examples:
|
|
121
|
+
|
|
122
|
+
- If the task touches login, token issuance, password reset, session refresh, or identity proofing, prioritize authentication, session, crypto, logging, and brute-force defense guardrails.
|
|
123
|
+
- If the task changes role checks, tenant scoping, admin APIs, resource ownership, or query filters, prioritize authorization, tenant isolation, data access, business-logic, and audit guardrails.
|
|
124
|
+
- If the task introduces parsing, uploads, template expansion, or object hydration, prioritize input validation, file handling, deserialization, and denial-of-service guardrails.
|
|
125
|
+
- If the task moves security decisions into the browser or mobile client, prioritize client-side trust, token storage, server-side revalidation, and privilege-boundary guardrails.
|
|
126
|
+
|
|
127
|
+
### Step 3: Hydrate exact shortlisted guardrails
|
|
128
|
+
|
|
129
|
+
For every shortlisted existing guardrail, call `get_guardrail_by_id` to retrieve the exact guardrail that will govern implementation.
|
|
130
|
+
|
|
131
|
+
- Use `get_guardrail_by_id` for the shortlisted ids only.
|
|
132
|
+
- If the tool supports batching, batch the shortlisted ids.
|
|
133
|
+
- If the tool only supports one id at a time, call it once per shortlisted id.
|
|
134
|
+
|
|
135
|
+
Implementation must be driven by the hydrated shortlist from `get_guardrail_by_id`, not by vague memory from the broad catalog listing.
|
|
136
|
+
|
|
137
|
+
### Step 4: Track the active shortlist in context
|
|
138
|
+
|
|
139
|
+
Maintain an explicit in-context list of the shortlisted existing guardrails that will govern the task. For each shortlisted existing guardrail, keep:
|
|
140
|
+
|
|
141
|
+
- `id`
|
|
142
|
+
- `title`
|
|
143
|
+
- `rule_type`
|
|
144
|
+
- `category`
|
|
145
|
+
- `instruction`
|
|
146
|
+
- `why_selected`
|
|
147
|
+
|
|
148
|
+
Also track any new guardrails created during the task as `ide_generated`.
|
|
149
|
+
|
|
150
|
+
This shortlist is the source of truth for the rest of the session.
|
|
151
|
+
|
|
152
|
+
## Implementation Rules
|
|
153
|
+
|
|
154
|
+
Once the shortlist is hydrated:
|
|
155
|
+
|
|
156
|
+
- Every applicable `must` guardrail is mandatory.
|
|
157
|
+
- Every applicable `must_not` guardrail is a hard prohibition.
|
|
158
|
+
- If two shortlisted guardrails appear to conflict, explain the conflict and resolve it before coding.
|
|
159
|
+
- If the task reveals a real gap not covered by the shortlisted existing guardrails, create an `ide_generated` guardrail and apply it immediately.
|
|
160
|
+
|
|
161
|
+
When deciding whether a guardrail applies, prefer security-preserving inclusion over risky omission. If it plausibly mitigates a realistic path to abuse for the current task, keep it in scope.
|
|
162
|
+
|
|
163
|
+
## CTM Sync Handoff Contract
|
|
164
|
+
|
|
165
|
+
`ctm_sync` must reuse the shortlist from this skill. It must not call `get_guardrails` or `get_guardrail_by_id` again.
|
|
166
|
+
|
|
167
|
+
Before `ctm_sync` is invoked, ensure the parent context clearly contains:
|
|
168
|
+
|
|
169
|
+
- the exact existing guardrails shortlisted earlier
|
|
170
|
+
- which of them were applied
|
|
171
|
+
- whether each one was satisfied
|
|
172
|
+
- any notes about partial compliance, conflicts, or rationale
|
|
173
|
+
- every `ide_generated` guardrail created during the task
|
|
174
|
+
|
|
175
|
+
If a guardrail was shortlisted but not fully satisfied, still include it in the handoff with `satisfied: false` and a note. Do not silently drop it.
|
|
176
|
+
|
|
177
|
+
## Selection Quality Bar
|
|
178
|
+
|
|
179
|
+
A good selection does all of the following:
|
|
180
|
+
|
|
181
|
+
- covers the feature’s real threat surface, not just its visible functionality
|
|
182
|
+
- captures adjacent controls that stop bypass chains
|
|
183
|
+
- avoids irrelevant noise
|
|
184
|
+
- produces a small, defensible set of guardrails that can actually guide implementation
|
|
185
|
+
- leaves `ctm_sync` with an exact list of what the IDE selected and enforced
|
|
186
|
+
|
|
187
|
+
If your shortlist feels generic, it is probably incomplete or over-broad. Re-check the prompt, the code patterns, and the threat map.
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# Guardrail Selection Threat Map
|
|
2
|
+
|
|
3
|
+
Use this file when deciding which guardrail categories apply to the current task and which threat families should influence the shortlist.
|
|
4
|
+
|
|
5
|
+
The intent is not to produce a full threat model here. The intent is to make sure likely exploit paths influence which guardrails are fetched by id and enforced during implementation.
|
|
6
|
+
|
|
7
|
+
## How to use this map
|
|
8
|
+
|
|
9
|
+
1. Start from the feature or code change.
|
|
10
|
+
2. Infer the categories involved in the implementation.
|
|
11
|
+
3. Use the mappings below to identify likely threat families.
|
|
12
|
+
4. Shortlist guardrails that directly or adjacently mitigate those threats.
|
|
13
|
+
5. Fetch those guardrails with `get_guardrail_by_id`.
|
|
14
|
+
|
|
15
|
+
## STRIDE-style mappings for guardrail selection
|
|
16
|
+
|
|
17
|
+
### Spoofing
|
|
18
|
+
|
|
19
|
+
Usually maps to authentication and session-related controls.
|
|
20
|
+
|
|
21
|
+
Threat patterns to consider:
|
|
22
|
+
|
|
23
|
+
- identity-related attacks
|
|
24
|
+
- session and token attacks
|
|
25
|
+
- business-logic attacks leading to authentication bypass
|
|
26
|
+
- injection attacks leading to authentication bypass
|
|
27
|
+
- serialization attacks leading to authentication bypass
|
|
28
|
+
- cryptographic attacks leading to authentication bypass
|
|
29
|
+
- lapses in logging leading to authentication bypass
|
|
30
|
+
- client-side trust leading to authentication bypass
|
|
31
|
+
|
|
32
|
+
Categories commonly shortlisted:
|
|
33
|
+
|
|
34
|
+
- `authentication`
|
|
35
|
+
- `session_management`
|
|
36
|
+
- `cryptography`
|
|
37
|
+
- `logging`
|
|
38
|
+
- `client_side`
|
|
39
|
+
- `business_logic`
|
|
40
|
+
- `input_validation`
|
|
41
|
+
- `deserialization`
|
|
42
|
+
|
|
43
|
+
### Tampering
|
|
44
|
+
|
|
45
|
+
Usually maps to authorization, integrity, and unsafe state change controls.
|
|
46
|
+
|
|
47
|
+
Threat patterns to consider:
|
|
48
|
+
|
|
49
|
+
- broken object level access control patterns
|
|
50
|
+
- broken functional level access control patterns
|
|
51
|
+
- injection-driven authorization bypass
|
|
52
|
+
- serialization-driven authorization bypass
|
|
53
|
+
- business-logic-driven authorization bypass
|
|
54
|
+
- client-side trust leading to unauthorized state changes
|
|
55
|
+
|
|
56
|
+
Categories commonly shortlisted:
|
|
57
|
+
|
|
58
|
+
- `authorization`
|
|
59
|
+
- `tenant_isolation`
|
|
60
|
+
- `data_access`
|
|
61
|
+
- `business_logic`
|
|
62
|
+
- `logging`
|
|
63
|
+
- `input_validation`
|
|
64
|
+
- `deserialization`
|
|
65
|
+
- `client_side`
|
|
66
|
+
|
|
67
|
+
### Repudiation
|
|
68
|
+
|
|
69
|
+
Usually appears when spoofing and tampering are possible but the system cannot prove what happened.
|
|
70
|
+
|
|
71
|
+
Threat patterns to consider:
|
|
72
|
+
|
|
73
|
+
- weak or missing audit trails for auth and authorization decisions
|
|
74
|
+
- missing actor attribution on sensitive state changes
|
|
75
|
+
- mutable or incomplete event records
|
|
76
|
+
- inability to correlate session, actor, and resource changes
|
|
77
|
+
|
|
78
|
+
Categories commonly shortlisted:
|
|
79
|
+
|
|
80
|
+
- `logging`
|
|
81
|
+
- `monitoring`
|
|
82
|
+
- `authentication`
|
|
83
|
+
- `authorization`
|
|
84
|
+
- `admin_workflows`
|
|
85
|
+
|
|
86
|
+
### Information Disclosure
|
|
87
|
+
|
|
88
|
+
Usually maps to authorization, data exposure, logging, and unsafe client-side trust.
|
|
89
|
+
|
|
90
|
+
Threat patterns to consider:
|
|
91
|
+
|
|
92
|
+
- broken object level access control patterns
|
|
93
|
+
- broken functional level access control patterns
|
|
94
|
+
- injection-driven information disclosure
|
|
95
|
+
- serialization-driven information disclosure
|
|
96
|
+
- business-logic-driven information disclosure
|
|
97
|
+
- lapses in logging leading to disclosure
|
|
98
|
+
- client-side trust causing exposure of protected data
|
|
99
|
+
|
|
100
|
+
Categories commonly shortlisted:
|
|
101
|
+
|
|
102
|
+
- `authorization`
|
|
103
|
+
- `tenant_isolation`
|
|
104
|
+
- `data_access`
|
|
105
|
+
- `logging`
|
|
106
|
+
- `input_validation`
|
|
107
|
+
- `deserialization`
|
|
108
|
+
- `client_side`
|
|
109
|
+
- `output_encoding`
|
|
110
|
+
|
|
111
|
+
### Denial of Service
|
|
112
|
+
|
|
113
|
+
Usually maps to workload protection, parsing safety, quota controls, and expensive query behavior.
|
|
114
|
+
|
|
115
|
+
Threat patterns to consider:
|
|
116
|
+
|
|
117
|
+
- broken access control patterns that expose heavy operations
|
|
118
|
+
- injection or data-access paths that amplify resource consumption
|
|
119
|
+
- serialization-driven memory or parser exhaustion
|
|
120
|
+
- business-logic-driven abuse of expensive workflows
|
|
121
|
+
- logging lapses that hide repeated abuse
|
|
122
|
+
|
|
123
|
+
Categories commonly shortlisted:
|
|
124
|
+
|
|
125
|
+
- `rate_limiting`
|
|
126
|
+
- `input_validation`
|
|
127
|
+
- `file_uploads`
|
|
128
|
+
- `deserialization`
|
|
129
|
+
- `data_access`
|
|
130
|
+
- `network`
|
|
131
|
+
- `monitoring`
|
|
132
|
+
- `logging`
|
|
133
|
+
- `business_logic`
|
|
134
|
+
|
|
135
|
+
### Elevation of Privilege
|
|
136
|
+
|
|
137
|
+
Usually maps to authorization, role boundaries, trust decisions, and privileged workflow controls.
|
|
138
|
+
|
|
139
|
+
Threat patterns to consider:
|
|
140
|
+
|
|
141
|
+
- access control bypasses from broken object or function level access
|
|
142
|
+
- injection-based privilege escalation
|
|
143
|
+
- client-side induced privilege escalation
|
|
144
|
+
- serialization-induced privilege escalation
|
|
145
|
+
- business-logic-triggered privilege escalation
|
|
146
|
+
- logging lapses that conceal privilege abuse
|
|
147
|
+
|
|
148
|
+
Categories commonly shortlisted:
|
|
149
|
+
|
|
150
|
+
- `authorization`
|
|
151
|
+
- `tenant_isolation`
|
|
152
|
+
- `admin_workflows`
|
|
153
|
+
- `business_logic`
|
|
154
|
+
- `client_side`
|
|
155
|
+
- `input_validation`
|
|
156
|
+
- `deserialization`
|
|
157
|
+
- `logging`
|
|
158
|
+
|
|
159
|
+
## Fast examples
|
|
160
|
+
|
|
161
|
+
### Add password reset flow
|
|
162
|
+
|
|
163
|
+
Likely categories:
|
|
164
|
+
|
|
165
|
+
- `authentication`
|
|
166
|
+
- `session_management`
|
|
167
|
+
- `cryptography`
|
|
168
|
+
- `logging`
|
|
169
|
+
- `rate_limiting`
|
|
170
|
+
|
|
171
|
+
Likely threat families:
|
|
172
|
+
|
|
173
|
+
- spoofing
|
|
174
|
+
- repudiation
|
|
175
|
+
- information disclosure
|
|
176
|
+
|
|
177
|
+
### Add admin endpoint to change user role
|
|
178
|
+
|
|
179
|
+
Likely categories:
|
|
180
|
+
|
|
181
|
+
- `authorization`
|
|
182
|
+
- `tenant_isolation`
|
|
183
|
+
- `admin_workflows`
|
|
184
|
+
- `logging`
|
|
185
|
+
- `business_logic`
|
|
186
|
+
|
|
187
|
+
Likely threat families:
|
|
188
|
+
|
|
189
|
+
- tampering
|
|
190
|
+
- repudiation
|
|
191
|
+
- elevation of privilege
|
|
192
|
+
- information disclosure
|
|
193
|
+
|
|
194
|
+
### Add bulk import endpoint
|
|
195
|
+
|
|
196
|
+
Likely categories:
|
|
197
|
+
|
|
198
|
+
- `input_validation`
|
|
199
|
+
- `file_uploads`
|
|
200
|
+
- `deserialization`
|
|
201
|
+
- `data_access`
|
|
202
|
+
- `logging`
|
|
203
|
+
- `rate_limiting`
|
|
204
|
+
|
|
205
|
+
Likely threat families:
|
|
206
|
+
|
|
207
|
+
- tampering
|
|
208
|
+
- information disclosure
|
|
209
|
+
- denial of service
|
|
210
|
+
|
|
211
|
+
### Move entitlement checks to the frontend
|
|
212
|
+
|
|
213
|
+
Likely categories:
|
|
214
|
+
|
|
215
|
+
- `authorization`
|
|
216
|
+
- `client_side`
|
|
217
|
+
- `tenant_isolation`
|
|
218
|
+
- `logging`
|
|
219
|
+
|
|
220
|
+
Likely threat families:
|
|
221
|
+
|
|
222
|
+
- tampering
|
|
223
|
+
- information disclosure
|
|
224
|
+
- elevation of privilege
|
|
225
|
+
|
|
226
|
+
## Selection reminders
|
|
227
|
+
|
|
228
|
+
- A feature can require guardrails from multiple categories.
|
|
229
|
+
- Shortlist for exploit chains, not isolated weaknesses.
|
|
230
|
+
- Logging often matters because poor auditability can turn spoofing, tampering, or privilege abuse into repudiation.
|
|
231
|
+
- Client-side logic often needs server-side guardrails even if the visible change is in the UI.
|
|
232
|
+
- If no existing guardrail covers a realistic recurring threat, create an `ide_generated` guardrail and carry it into `ctm_sync`.
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Vibe Guardrails — fetch and enforce project-specific secure coding guardrails from SRAI
|
|
3
|
-
alwaysApply: true
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# Vibe Guardrails — Secure by Code
|
|
7
2
|
|
|
8
3
|
**Enforce project-specific security guardrails during every code-generation task.**
|
|
9
4
|
|
|
10
|
-
This rule complements the PWNISMS threat model ("secure by design") with concrete, project-level coding dos and don'ts ("secure by code"). Guardrails are maintained in SRAI and fetched via
|
|
5
|
+
This rule complements the PWNISMS threat model ("secure by design") with concrete, project-level coding dos and don'ts ("secure by code"). Guardrails are maintained in SRAI and fetched via `security-review-mcp`.
|
|
11
6
|
|
|
12
7
|
---
|
|
13
8
|
|
|
@@ -28,21 +23,32 @@ Skip guardrail enforcement for tasks with **no code output** (documentation, Q&A
|
|
|
28
23
|
|
|
29
24
|
## Required behavior
|
|
30
25
|
|
|
31
|
-
### 1.
|
|
26
|
+
### 1. Use the guardrails-selection skill at the start of every code task
|
|
27
|
+
|
|
28
|
+
Before writing or modifying code, you must read and follow `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md`.
|
|
32
29
|
|
|
33
|
-
|
|
30
|
+
That skill is mandatory because the IDE must not leave guardrail selection to chance. The required workflow is:
|
|
34
31
|
|
|
35
|
-
- **
|
|
36
|
-
- **
|
|
37
|
-
-
|
|
32
|
+
- **Analyze the task first** — infer what the user is actually building, what components are touched, what categories are involved, and what threats might occur.
|
|
33
|
+
- **Resolve the project** — use `find_project_by_name` with `name="<SRAI_PROJECT_NAME>"` to obtain `project_id`.
|
|
34
|
+
- **Load the broad catalog** — call `get_guardrails` with `project_id`.
|
|
35
|
+
- **Shortlist intentionally** — choose only the guardrails that mitigate the categories and likely threats for the current task.
|
|
36
|
+
- **Hydrate the exact shortlist** — call `get_guardrail_by_id` for the shortlisted guardrail ids so implementation uses the exact selected guardrails, not a vague reading of the full catalog.
|
|
37
|
+
- **Preserve the shortlist** — keep the selected existing guardrails in context so `ctm_sync` can include the same guardrails later without re-querying.
|
|
38
|
+
|
|
39
|
+
The broad `get_guardrails` result is only the candidate catalog. The active implementation guardrails must come from the shortlisted `get_guardrail_by_id` result.
|
|
40
|
+
|
|
41
|
+
Each guardrail used for implementation should preserve these fields:
|
|
38
42
|
|
|
39
43
|
| Field | Description |
|
|
40
44
|
|---|---|
|
|
45
|
+
| `id` | Stable guardrail identifier used with `get_guardrail_by_id` |
|
|
41
46
|
| `title` | Short name of the guardrail |
|
|
42
47
|
| `rule_type` | `must` (mandatory) or `must_not` (prohibition) |
|
|
43
48
|
| `category` | Grouping label (e.g. `authentication`, `input_validation`, `secrets`) or `null` |
|
|
44
49
|
| `instruction` | The actionable coding directive |
|
|
45
|
-
|
|
50
|
+
|
|
51
|
+
If `get_guardrails` returns additional metadata such as `source_ref`, use it as supporting context during selection.
|
|
46
52
|
|
|
47
53
|
### 2. Apply guardrails during code generation
|
|
48
54
|
|
|
@@ -65,6 +71,7 @@ After code generation, include a brief guardrails compliance summary:
|
|
|
65
71
|
|
|
66
72
|
- List which guardrails were applied (by title), distinguishing existing vs IDE-generated.
|
|
67
73
|
- Flag any guardrails that could not be fully satisfied and explain why.
|
|
74
|
+
- Do not drop shortlisted existing guardrails from session context. `ctm_sync` must receive the same shortlist, including any unsatisfied items with notes.
|
|
68
75
|
|
|
69
76
|
---
|
|
70
77
|
|
|
@@ -82,5 +89,6 @@ Guardrails are living artifacts. The IDE agent can create, apply, and update the
|
|
|
82
89
|
|
|
83
90
|
| Tool | Purpose |
|
|
84
91
|
|---|---|
|
|
85
|
-
| `get_guardrails` | Fetch project guardrails (requires `project_id`) |
|
|
92
|
+
| `get_guardrails` | Fetch the broad project guardrails catalog (requires `project_id`) |
|
|
93
|
+
| `get_guardrail_by_id` | Fetch the exact shortlisted guardrails that will govern implementation |
|
|
86
94
|
| `find_project_by_name` | Resolve project by name to get `project_id` |
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"hooks": {
|
|
4
4
|
"sessionStart": [
|
|
5
5
|
{
|
|
6
|
-
"command": "printf '%s\\n' '{\"additional_context\":\"## MANDATORY SECURITY GATE (Session Policy)\\n\\nFor ANY user request that has security impact (auth, crypto, input handling, secrets, network, data storage, dependencies, new APIs/endpoints, or any code handling untrusted data):\\n\\n**Plan order:**\\n1. **Fetch Vibe Guardrails (first)** -
|
|
6
|
+
"command": "printf '%s\\n' '{\"additional_context\":\"## MANDATORY SECURITY GATE (Session Policy)\\n\\nFor ANY user request that has security impact (auth, crypto, input handling, secrets, network, data storage, dependencies, new APIs/endpoints, or any code handling untrusted data):\\n\\n**Plan order:**\\n1. **Fetch Vibe Guardrails (first)** - Use .cursor/skills/guardrails-selection/SKILL.md. Analyze the task, call get_guardrails from security-review-mcp to load the broad catalog, shortlist the relevant guardrails, then call get_guardrail_by_id for the shortlist. These exact shortlisted guardrails are the hard constraints on generated code.\\n2. **PWNISMS threat modeling (second)** - Run threat modeling as a guide before writing code. Use .cursor/skills/threat-modelling/SKILL.md. Cross-reference guardrails with PWNISMS findings to identify gaps. Document findings to inform secure implementation.\\n3. **Implement secure code (third)** - Use the threat model findings AND shortlisted guardrails as guides to create secure code. Every must guardrail must be satisfied; no must_not guardrail may be violated.\\n4. **ctm_sync (last)** - Push payload to SRAI only after implementation is done. Invoke ctm_sync agent (Task tool, subagent_type=ctm_sync) as the final step. Include the exact shortlisted guardrails from earlier plus any IDE-generated guardrails in the guardrails_applied payload field; do not query again during ctm_sync. See .cursor/agents/ctm_sync.md and .cursor/rules/ctm_sync_rule.mdc.\\n\\nNo blocking. No deferral. Guardrails enforce secure code; threat modeling guides the design; ctm_sync runs last to push to SRAI. Both ctm_sync and guardrail compliance are mandatory and should be part of your to-do.\"}'",
|
|
7
7
|
"timeout": 5
|
|
8
8
|
}
|
|
9
9
|
]
|
|
10
10
|
}
|
|
11
|
-
}
|
|
11
|
+
}
|
|
@@ -33,7 +33,7 @@ Before deep analysis, pull project context from `security-review-mcp` (if availa
|
|
|
33
33
|
- `get_data_dictionaries` — sensitive data assets
|
|
34
34
|
- `get_security_objectives` — compliance targets
|
|
35
35
|
- `get_findings` — aggregated insights
|
|
36
|
-
10. **Vibe Guardrails** — `
|
|
36
|
+
10. **Vibe Guardrails** — Use `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md` with `project_id` to load the guardrail catalog, shortlist the relevant guardrails, and hydrate the exact implementation set with `get_guardrail_by_id`.
|
|
37
37
|
|
|
38
38
|
If SRAI is not available, proceed with whatever context the user provides — files, diffs, PRs, architecture docs.
|
|
39
39
|
|
|
@@ -47,7 +47,7 @@ Collect these quickly before deep analysis:
|
|
|
47
47
|
- **Assets**: What must be protected (PII, credentials, tokens, configs, accounts, workflows)?
|
|
48
48
|
- **Entry points**: How data enters/leaves (HTTP, queues, schedulers, CLI, webhooks, integrations)?
|
|
49
49
|
- **Trust boundaries**: Where data crosses users/services/networks/privilege levels?
|
|
50
|
-
- **Existing guardrails**: What project-specific dos and don'ts apply (from Phase 0, step 10)?
|
|
50
|
+
- **Existing guardrails**: What shortlisted project-specific dos and don'ts apply (from Phase 0, step 10)?
|
|
51
51
|
|
|
52
52
|
If the user provided specific code, diffs, or architecture artifacts, prioritize those as primary evidence.
|
|
53
53
|
|
|
@@ -164,8 +164,8 @@ Dependency and delivery threats:
|
|
|
164
164
|
|
|
165
165
|
After completing the PWNISMS analysis and before writing code:
|
|
166
166
|
|
|
167
|
-
1. **Review
|
|
168
|
-
2. **Classify applicability** — For each guardrail, determine if it applies to the current task.
|
|
167
|
+
1. **Review the shortlisted hydrated guardrails** produced by `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md`.
|
|
168
|
+
2. **Classify applicability** — For each shortlisted guardrail, determine if it applies to the current task.
|
|
169
169
|
3. **Apply during code generation:**
|
|
170
170
|
- `must` rules → mandatory implementation requirements. Every applicable `must` guardrail must be satisfied.
|
|
171
171
|
- `must_not` rules → hard prohibitions. Code must never violate an applicable `must_not` guardrail.
|
|
@@ -224,7 +224,7 @@ The `ctm_sync` agent builds and pushes an event payload containing:
|
|
|
224
224
|
- **Threat model findings**: threats mitigated, PWNISMS categories, severities, mitigations applied
|
|
225
225
|
- **Best practices achieved**: security patterns followed during implementation
|
|
226
226
|
- **Secure code snippets**: security-relevant code with explanations
|
|
227
|
-
- **Guardrails applied**: all guardrails enforced during this session — both existing ones
|
|
227
|
+
- **Guardrails applied**: all guardrails enforced during this session — both existing ones shortlisted earlier via `get_guardrails` + `get_guardrail_by_id` (`source: "existing"`) and new ones the IDE agent created on the fly (`source: "ide_generated"`), each with satisfaction status
|
|
228
228
|
- **Project profile updates**: architecture notes, tech categories, user groups, compliance requirements, language stacks
|
|
229
229
|
|
|
230
230
|
### How to invoke
|
package/src/utils/constants.js
CHANGED
|
@@ -66,5 +66,12 @@ export const GUARDRAILS_PROFILER_SKILL_REL_DIR = {
|
|
|
66
66
|
codex: '.codex/skills/guardrails-profiler',
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
+
/** Relative workspace dirs for the guardrails-selection skill (per IDE / CLI). */
|
|
70
|
+
export const GUARDRAILS_SELECTION_SKILL_REL_DIR = {
|
|
71
|
+
cursor: '.cursor/skills/guardrails-selection',
|
|
72
|
+
claude: '.claude/skills/guardrails-selection',
|
|
73
|
+
codex: '.codex/skills/guardrails-selection',
|
|
74
|
+
};
|
|
75
|
+
|
|
69
76
|
export const SENTINEL_START = '<!-- securityreview-kit:start -->';
|
|
70
77
|
export const SENTINEL_END = '<!-- securityreview-kit:end -->';
|
|
@@ -1,7 +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
|
+
import { GUARDRAILS_PROFILER_SKILL_REL_DIR, GUARDRAILS_SELECTION_SKILL_REL_DIR } from './constants.js';
|
|
5
5
|
import { ensureDir, writeText } from './fs-helpers.js';
|
|
6
6
|
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -16,42 +16,69 @@ function injectProjectName(content, projectName) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function injectSkillDir(content, skillDirRel) {
|
|
19
|
-
return content
|
|
19
|
+
return content
|
|
20
|
+
.replaceAll('<GUARDRAILS_SKILL_DIR>', skillDirRel)
|
|
21
|
+
.replaceAll('{{GUARDRAILS_SELECTION_SKILL_DIR}}', skillDirRel);
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
function injectSkillTemplate(content, projectName, skillDirRel) {
|
|
23
25
|
return injectSkillDir(injectProjectName(content, projectName), skillDirRel);
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
const
|
|
28
|
+
const PROFILER_BUNDLE_ROOT = join(__dirname, '..', 'generators', 'rules', 'guardrails-profiler');
|
|
29
|
+
const SELECTION_BUNDLE_ROOT = join(__dirname, '..', 'generators', 'rules', 'guardrails-selection');
|
|
27
30
|
|
|
28
31
|
/**
|
|
29
|
-
* Writes
|
|
30
|
-
* IDE skills directory (e.g. `.cursor/skills/guardrails-profiler`, `.claude/skills/...`).
|
|
32
|
+
* Writes bundled guardrails skills under each selected IDE skills directory.
|
|
31
33
|
*
|
|
32
34
|
* @returns {string[]} Absolute paths to each skill root written */
|
|
33
|
-
export function
|
|
35
|
+
export function writeGuardrailsSkillBundles(cwd, options = {}) {
|
|
34
36
|
const targets = Array.isArray(options.targets) ? options.targets : [];
|
|
35
37
|
const written = [];
|
|
36
38
|
|
|
37
39
|
for (const target of targets) {
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
+
const profilerRel = GUARDRAILS_PROFILER_SKILL_REL_DIR[target];
|
|
41
|
+
if (profilerRel) {
|
|
42
|
+
const profilerDestBase = join(cwd, profilerRel);
|
|
43
|
+
const profilerDestRefs = join(profilerDestBase, 'references');
|
|
44
|
+
ensureDir(profilerDestRefs);
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
const profilerSkillTemplate = readFileSync(join(PROFILER_BUNDLE_ROOT, 'SKILL.md'), 'utf-8');
|
|
47
|
+
writeText(
|
|
48
|
+
join(profilerDestBase, 'SKILL.md'),
|
|
49
|
+
injectSkillTemplate(profilerSkillTemplate, options.projectName, profilerRel),
|
|
50
|
+
);
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
copyFileSync(
|
|
53
|
+
join(PROFILER_BUNDLE_ROOT, 'references', 'signal-registry.json'),
|
|
54
|
+
join(profilerDestRefs, 'signal-registry.json'),
|
|
55
|
+
);
|
|
47
56
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
join(destRefs, 'signal-registry.json'),
|
|
51
|
-
);
|
|
57
|
+
written.push(profilerDestBase);
|
|
58
|
+
}
|
|
52
59
|
|
|
53
|
-
|
|
60
|
+
const selectionRel = GUARDRAILS_SELECTION_SKILL_REL_DIR[target];
|
|
61
|
+
if (selectionRel) {
|
|
62
|
+
const selectionDestBase = join(cwd, selectionRel);
|
|
63
|
+
const selectionDestRefs = join(selectionDestBase, 'references');
|
|
64
|
+
ensureDir(selectionDestRefs);
|
|
65
|
+
|
|
66
|
+
const selectionSkillTemplate = readFileSync(join(SELECTION_BUNDLE_ROOT, 'SKILL.md'), 'utf-8');
|
|
67
|
+
writeText(
|
|
68
|
+
join(selectionDestBase, 'SKILL.md'),
|
|
69
|
+
injectSkillTemplate(selectionSkillTemplate, options.projectName, selectionRel),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
copyFileSync(
|
|
73
|
+
join(SELECTION_BUNDLE_ROOT, 'references', 'category-threat-map.md'),
|
|
74
|
+
join(selectionDestRefs, 'category-threat-map.md'),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
written.push(selectionDestBase);
|
|
78
|
+
}
|
|
54
79
|
}
|
|
55
80
|
|
|
56
81
|
return written;
|
|
57
82
|
}
|
|
83
|
+
|
|
84
|
+
export const writeGuardrailsProfilerBundles = writeGuardrailsSkillBundles;
|