@securityreviewai/vibereview-cli 0.1.4 → 0.1.5

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/README.md CHANGED
@@ -110,18 +110,19 @@ VibeReview creates:
110
110
  ├── config.json
111
111
  ├── profile.json
112
112
  ├── guardrails.yml
113
+ ├── guardrails.md
113
114
  ├── state.json
114
115
  ├── gates/
115
116
  └── reports/
116
117
  ```
117
118
 
118
- The full catalog and generator skill remain inside the installed npm package. They are not copied into the target workspace. `guardrails.yml` contains only matched baseline, generated code-specific, and user-maintained custom rules.
119
+ The full catalog and generator skill remain inside the installed npm package. They are not copied into the target workspace. `guardrails.yml` is the canonical machine-readable source containing matched baseline, generated code-specific, and user-maintained custom rules. `guardrails.md` is an automatically regenerated, table-formatted view for humans; do not edit it directly.
119
120
 
120
121
  ## Code-specific generation
121
122
 
122
123
  VibeReview selects at most 30 security-relevant source files, capped at 80 KB in total and 24 KB per file. It excludes common dependency/build directories, provider configuration directories, `.env` files, credential-like paths, private keys, lockfiles, binary files, and symbolic links. The selected contents are sent through the configured provider CLI under that provider's own account and data-handling terms.
123
124
 
124
- The provider runs in a temporary empty directory rather than the repository. Cursor uses plan mode and sandboxing; Codex uses an ephemeral read-only execution; Claude Code runs in plan mode with tools disabled and no persisted session; Copilot runs non-interactively with only its read tool exposed to the empty directory. The model receives the bounded evidence inline and cannot edit the repository through this job.
125
+ The provider runs in a temporary empty directory rather than the repository. Cursor uses read-only ask mode and sandboxing; Codex uses an ephemeral read-only execution; Claude Code runs in plan mode with tools disabled and no persisted session; Copilot runs non-interactively with only its read tool exposed to the empty directory. The model receives the bounded evidence inline and cannot edit the repository through this job.
125
126
 
126
127
  Generated output must pass the bundled JSON schema. Every rule needs an exact evidence path from the supplied bundle, generic or baseline-duplicate rules are removed, and invalid output gets one repair attempt. A successful regeneration atomically replaces only `code_specific`; baseline and custom rules remain intact.
127
128
 
@@ -157,7 +158,7 @@ Reports live at:
157
158
 
158
159
  The IDE agent writes the report directly; the CLI does not generate a JSON scan. The session ID is the complete file identity, so feature title changes cannot create another report accidentally. A new IDE chat creates one Markdown file. Every security-relevant follow-up reads and rewrites that same file. The agent may add, revise, reorder, or remove content so the report describes the latest cumulative state rather than preserving a chronological log.
159
160
 
160
- Reports retain the established VibeReview fields in readable sections: summary, scope and assumptions, threats mitigated, best practices achieved, OSV.dev dependency security, secure code changes, guardrails applied, OWASP mappings, verification, and residual risks. YAML frontmatter carries session identity, provider, status, and timestamps.
161
+ Reports retain the established VibeReview fields in table-formatted sections: scope and assumptions, threats mitigated, best practices achieved, OSV.dev dependency security, secure code changes, guardrails applied, OWASP mappings, verification, and residual risks. YAML frontmatter carries session identity, provider, status, and timestamps.
161
162
 
162
163
  ## Deterministic detection
163
164
 
@@ -57,5 +57,6 @@ export async function generateCommand(options) {
57
57
  if (options.verbose)
58
58
  ui.detail(` Evidence bundle: ${result.evidence.total_bytes.toLocaleString()} bytes; ${result.evidence.omitted_file_count} candidate files omitted`);
59
59
  ui.detail(`Guardrails: ${path.relative(root, outputPath)}`);
60
+ ui.detail(`Readable: ${path.relative(root, path.join(root, ".vibereview", "guardrails.md"))}`);
60
61
  }
61
62
  //# sourceMappingURL=generate.js.map
@@ -118,6 +118,7 @@ export async function initCommand(options) {
118
118
  ui.info("\nWorkspace configured.");
119
119
  ui.detail(`Profile: ${path.relative(root, path.join(root, ".vibereview", "profile.json"))}`);
120
120
  ui.detail(`Guardrails: ${path.relative(root, path.join(root, ".vibereview", "guardrails.yml"))}`);
121
+ ui.detail(`Readable: ${path.relative(root, path.join(root, ".vibereview", "guardrails.md"))}`);
121
122
  ui.step(`Installed local security workflow for ${PROVIDER_NAMES[provider]}`);
122
123
  ui.detail(`Reports: ${path.relative(root, path.join(root, ".vibereview", "reports"))}/*.md`);
123
124
  ui.info("\nVibeReview is ready. Restart the IDE agent if it is already running.\n");
@@ -0,0 +1,58 @@
1
+ export function renderGuardrailsMarkdown(file) {
2
+ const sections = [
3
+ "<!-- Generated by VibeReview. Do not edit this file; edit guardrails.yml or use the CLI. -->",
4
+ `# VibeReview Guardrails — ${escapeInline(file.project || "Workspace")}`,
5
+ "",
6
+ "> Human-readable view generated from `.vibereview/guardrails.yml`. The YAML file remains the canonical source used by the CLI and IDE agents.",
7
+ "",
8
+ "## Overview",
9
+ "",
10
+ "| Source | Count |",
11
+ "|---|---:|",
12
+ `| Baseline packs | ${file.baseline.length} |`,
13
+ `| Code-specific | ${file.code_specific.length} |`,
14
+ `| Custom | ${file.custom.length} |`,
15
+ `| **Total** | **${file.baseline.length + file.code_specific.length + file.custom.length}** |`,
16
+ "",
17
+ `Generated: ${escapeInline(file.generated_at || "unknown")}`,
18
+ "",
19
+ renderSection("Baseline Guardrails", file.baseline),
20
+ renderSection("Code-Specific Guardrails", file.code_specific),
21
+ renderSection("Custom Guardrails", file.custom),
22
+ ];
23
+ return `${sections.join("\n").trimEnd()}\n`;
24
+ }
25
+ function renderSection(title, rules) {
26
+ const lines = [`## ${title}`, ""];
27
+ if (!rules.length)
28
+ return `${lines.join("\n")}None.`;
29
+ lines.push("| Guardrail | Requirement |", "|---|---|");
30
+ for (const rule of rules) {
31
+ const identity = [
32
+ `**${escapeCell(rule.title)}**`,
33
+ `\`${escapeCode(rule.id)}\``,
34
+ `${escapeCell(rule.category)} · \`${escapeCode(rule.type)}\``,
35
+ rule.pack ? `Pack: \`${escapeCode(rule.pack)}\`` : "",
36
+ rule.confidence ? `Confidence: ${escapeCell(rule.confidence)}` : "",
37
+ ].filter(Boolean).join("<br>");
38
+ const detail = [
39
+ escapeCell(rule.instruction),
40
+ rule.rationale ? `**Why:** ${escapeCell(rule.rationale)}` : "",
41
+ rule.evidence?.length ? `**Evidence:** ${rule.evidence.map((item) => `\`${escapeCode(item.path)}\` — ${escapeCell(item.reason)}`).join("<br>")}` : "",
42
+ rule.cwe_ids?.length ? `**CWE:** ${rule.cwe_ids.map((item) => `CWE-${escapeCell(item)}`).join(", ")}` : "",
43
+ rule.owasp_top10?.length ? `**OWASP:** ${rule.owasp_top10.map(escapeCell).join(", ")}` : "",
44
+ ].filter(Boolean).join("<br><br>");
45
+ lines.push(`| ${identity} | ${detail} |`);
46
+ }
47
+ return lines.join("\n");
48
+ }
49
+ function escapeInline(value) {
50
+ return value.replace(/[\r\n]+/g, " ").trim();
51
+ }
52
+ function escapeCell(value) {
53
+ return escapeInline(value).replace(/\|/g, "\\|");
54
+ }
55
+ function escapeCode(value) {
56
+ return escapeCell(value).replace(/`/g, "\\`");
57
+ }
58
+ //# sourceMappingURL=guardrail-markdown.js.map
@@ -6,6 +6,7 @@ import { SCHEMA_VERSION } from "../types.js";
6
6
  import { sha256, stableJson } from "./hash.js";
7
7
  import { writeFileAtomic } from "./fs.js";
8
8
  import { currentCommit } from "./repository.js";
9
+ import { renderGuardrailsMarkdown } from "./guardrail-markdown.js";
9
10
  export async function initializeWorkspace(input) {
10
11
  const workspaceDir = path.join(input.root, ".vibereview");
11
12
  const configPath = path.join(workspaceDir, "config.json");
@@ -43,6 +44,7 @@ export async function initializeWorkspace(input) {
43
44
  ["config.json", json(config)],
44
45
  ["profile.json", json(input.profile)],
45
46
  ["guardrails.yml", stringify(guardrailFile, { lineWidth: 100 })],
47
+ ["guardrails.md", renderGuardrailsMarkdown(guardrailFile)],
46
48
  ["state.json", json(state)],
47
49
  ];
48
50
  const workspaceExists = await exists(workspaceDir);
@@ -100,6 +102,7 @@ export async function updateCodeSpecificGuardrails(root, guardrailFile, codeSpec
100
102
  code_specific: codeSpecific,
101
103
  };
102
104
  await writeFileAtomic(filePath, stringify(updated, { lineWidth: 100 }));
105
+ await writeFileAtomic(path.join(root, ".vibereview", "guardrails.md"), renderGuardrailsMarkdown(updated));
103
106
  return filePath;
104
107
  }
105
108
  async function readGuardrails(filePath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@securityreviewai/vibereview-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Local-first security guardrails for AI coding agents.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,6 +31,8 @@ Feature names belong in `title` and `workflow_name`, not in the filename. Before
31
31
 
32
32
  The final report is a current-state document, not an event log.
33
33
 
34
+ Use the compact table layouts in the report contract for structured records. Tables must improve scanning in an IDE Markdown preview: keep cells short, escape pipe characters, use `<br>` for intentional in-cell breaks, and move substantial explanation below the table when needed. Do not force narrative context into an unreadably wide row.
35
+
34
36
  If more than one report is already associated with the current session ID, do not create another. Continue with the canonical `<chat-session-id>.md` path and disclose the pre-existing duplicates; do not delete user files without explicit authority.
35
37
 
36
38
  ## Accuracy rules
@@ -23,71 +23,73 @@ updated_at: "<ISO-8601 timestamp>"
23
23
 
24
24
  ## Scope and Assumptions
25
25
 
26
- - **Scope:** ...
27
- - **Assets:** ...
28
- - **Entry points:** ...
29
- - **Trust boundaries:** ...
30
- - **Assumptions:** ...
26
+ | Area | Current state |
27
+ |---|---|
28
+ | Scope | ... |
29
+ | Assets | ... |
30
+ | Entry points | ... |
31
+ | Trust boundaries | ... |
32
+ | Assumptions | ... |
31
33
 
32
34
  ## Threats Mitigated
33
35
 
34
- ### T-01 <Threat title>
35
-
36
- - **PWNISMS category:** Product | Workload | Network | IAM | Secrets | Monitoring | Supply Chain
37
- - **Scenario:** <actor/action/impact>
38
- - **Likelihood:** low | medium | high
39
- - **Impact:** low | medium | high | critical
40
- - **Status:** mitigated | partial | open | not_applicable
41
- - **Mitigation:** <implemented control>
42
- - **Guardrails:** <IDs or None>
43
- - **Evidence:** <repository paths/tests/configuration>
36
+ | ID and category | Scenario and risk | Current control | Status and evidence |
37
+ |---|---|---|---|
38
+ | **T-01 <title>**<br>Product \| Workload \| Network \| IAM \| Secrets \| Monitoring \| Supply Chain | <actor/action/impact><br>Likelihood: low \| medium \| high<br>Impact: low \| medium \| high \| critical | <implemented mitigation><br>Guardrails: <IDs or None> | `mitigated` \| `partial` \| `open` \| `not_applicable`<br><paths/tests/configuration> |
44
39
 
45
40
  ## Best Practices Achieved
46
41
 
47
- - **<Practice>:** <what was implemented and where>
42
+ | Practice | Implementation and evidence |
43
+ |---|---|
44
+ | <Practice> | <what was implemented and where> |
48
45
 
49
46
  ## Dependency Security (OSV.dev)
50
47
 
51
- ### `<ecosystem>:<package>@<evaluated version>`
52
-
53
- - **Change:** added | upgraded | downgraded | replaced
54
- - **Result:** clear | remediated | residual_risk | unverified
55
- - **Advisories:** <OSV IDs and severity, or None found>
56
- - **Decision:** <accepted version, replacement, removal, or unresolved reason>
57
- - **Verification:** <initial scan and required re-scan outcome>
48
+ | Dependency and change | Result | Advisories | Decision and verification |
49
+ |---|---|---|---|
50
+ | `<ecosystem>:<package>@<version>`<br>added \| upgraded \| downgraded \| replaced | `clear` \| `remediated` \| `residual_risk` \| `unverified` | <OSV IDs and severity, or None found> | <accepted version, replacement, removal, or unresolved reason><br><initial scan and required re-scan outcome> |
58
51
 
59
52
  Use `Not applicable — no dependency changes` when the feature did not change dependencies. Never use `clear` for a failed query, unknown severity, missing exact version, or an unscanned remediation version.
60
53
 
61
54
  ## Secure Code Changes
62
55
 
63
- ### `<repository-relative path>`
64
-
65
- <Short explanation of the security-relevant change. Include a small fenced excerpt only when it materially helps the reader.>
56
+ | Path | Security-relevant change |
57
+ |---|---|
58
+ | `<repository-relative path>` | <what changed and which control it implements> |
66
59
 
67
60
  ## Guardrails Applied
68
61
 
69
- ### `<guardrail id>` <title>
70
-
71
- - **Source:** pack | code_generated | custom
72
- - **Type:** must | must_not
73
- - **Category:** ...
74
- - **Satisfied:** yes | no
75
- - **Application:** <how the implementation complied, or why it did not>
76
- - **Evidence:** <paths/tests>
62
+ | Guardrail | Application | Satisfied | Evidence |
63
+ |---|---|:---:|---|
64
+ | **<title>**<br>`<guardrail id>`<br>pack \| code_generated \| custom · must \| must_not · <category> | <how the implementation complied, or why it did not> | yes \| no | <paths/tests> |
77
65
 
78
66
  ## OWASP Mappings
79
67
 
80
- - **<OWASP identifier and title>:** <mapped threats and controls>
68
+ | OWASP identifier | Mapped threats and controls |
69
+ |---|---|
70
+ | <identifier and title> | <threat IDs and implemented controls> |
81
71
 
82
72
  ## Verification
83
73
 
84
- - `<command or check>` — passed | failed | not run — <important result>
74
+ | Check | Result | Important evidence |
75
+ |---|:---:|---|
76
+ | `<command or check>` | passed \| failed \| not run | <important result> |
85
77
 
86
78
  ## Residual Risks
87
79
 
88
- - <Remaining exposure, operational dependency, deferred decision, or `None identified within the reviewed scope`.>
80
+ | Risk | Exposure and next action |
81
+ |---|---|
82
+ | <short risk title> | <remaining exposure, operational dependency, or deferred decision> |
89
83
  ```
90
84
 
85
+ ## Table formatting rules
86
+
87
+ - Keep one logical record per row. Use `<br>` for short line breaks inside a cell; never place a literal newline inside a table row.
88
+ - Escape literal pipe characters as `\|` so they do not create accidental columns.
89
+ - Keep cells concise. Put a short code excerpt below the relevant table only when it materially improves verification.
90
+ - Remove a table or row when it is no longer relevant. For a considered area with no findings, use one explicit row such as `None within the reviewed scope` rather than an empty table.
91
+ - Prefer repository-relative links or backticked paths. Do not place large code blocks, raw tool output, or a chronological change log in a cell.
92
+
91
93
  ## Field semantics
92
94
 
93
95
  - `workflow_name` remains stable in follow-ups even if `title` changes as scope becomes clearer.