opena2a-cli 0.1.2 → 0.3.0

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.
Files changed (79) hide show
  1. package/README.md +225 -1
  2. package/dist/commands/guard-hooks.d.ts +27 -0
  3. package/dist/commands/guard-hooks.d.ts.map +1 -0
  4. package/dist/commands/guard-hooks.js +207 -0
  5. package/dist/commands/guard-hooks.js.map +1 -0
  6. package/dist/commands/guard-policy.d.ts +54 -0
  7. package/dist/commands/guard-policy.d.ts.map +1 -0
  8. package/dist/commands/guard-policy.js +251 -0
  9. package/dist/commands/guard-policy.js.map +1 -0
  10. package/dist/commands/guard-signing.d.ts +52 -0
  11. package/dist/commands/guard-signing.d.ts.map +1 -0
  12. package/dist/commands/guard-signing.js +185 -0
  13. package/dist/commands/guard-signing.js.map +1 -0
  14. package/dist/commands/guard-snapshots.d.ts +54 -0
  15. package/dist/commands/guard-snapshots.d.ts.map +1 -0
  16. package/dist/commands/guard-snapshots.js +346 -0
  17. package/dist/commands/guard-snapshots.js.map +1 -0
  18. package/dist/commands/guard.d.ts +60 -4
  19. package/dist/commands/guard.d.ts.map +1 -1
  20. package/dist/commands/guard.js +475 -95
  21. package/dist/commands/guard.js.map +1 -1
  22. package/dist/commands/init.js +3 -4
  23. package/dist/commands/init.js.map +1 -1
  24. package/dist/commands/review.d.ts +110 -0
  25. package/dist/commands/review.d.ts.map +1 -0
  26. package/dist/commands/review.js +634 -0
  27. package/dist/commands/review.js.map +1 -0
  28. package/dist/commands/shield.d.ts +3 -0
  29. package/dist/commands/shield.d.ts.map +1 -1
  30. package/dist/commands/shield.js +488 -34
  31. package/dist/commands/shield.js.map +1 -1
  32. package/dist/index.js +36 -6
  33. package/dist/index.js.map +1 -1
  34. package/dist/report/review-html.d.ts +16 -0
  35. package/dist/report/review-html.d.ts.map +1 -0
  36. package/dist/report/review-html.js +579 -0
  37. package/dist/report/review-html.js.map +1 -0
  38. package/dist/router.d.ts.map +1 -1
  39. package/dist/router.js +2 -1
  40. package/dist/router.js.map +1 -1
  41. package/dist/shield/ai-tool-config.d.ts +49 -0
  42. package/dist/shield/ai-tool-config.d.ts.map +1 -0
  43. package/dist/shield/ai-tool-config.js +169 -0
  44. package/dist/shield/ai-tool-config.js.map +1 -0
  45. package/dist/shield/arp-bridge.d.ts +62 -0
  46. package/dist/shield/arp-bridge.d.ts.map +1 -0
  47. package/dist/shield/arp-bridge.js +198 -0
  48. package/dist/shield/arp-bridge.js.map +1 -0
  49. package/dist/shield/baselines.d.ts +58 -0
  50. package/dist/shield/baselines.d.ts.map +1 -0
  51. package/dist/shield/baselines.js +371 -0
  52. package/dist/shield/baselines.js.map +1 -0
  53. package/dist/shield/findings.d.ts +52 -0
  54. package/dist/shield/findings.d.ts.map +1 -0
  55. package/dist/shield/findings.js +336 -0
  56. package/dist/shield/findings.js.map +1 -0
  57. package/dist/shield/init.d.ts +3 -0
  58. package/dist/shield/init.d.ts.map +1 -1
  59. package/dist/shield/init.js +145 -12
  60. package/dist/shield/init.js.map +1 -1
  61. package/dist/shield/integrity.d.ts.map +1 -1
  62. package/dist/shield/integrity.js +6 -2
  63. package/dist/shield/integrity.js.map +1 -1
  64. package/dist/shield/report-html.d.ts +29 -0
  65. package/dist/shield/report-html.d.ts.map +1 -0
  66. package/dist/shield/report-html.js +689 -0
  67. package/dist/shield/report-html.js.map +1 -0
  68. package/dist/shield/sarif.d.ts +65 -0
  69. package/dist/shield/sarif.d.ts.map +1 -0
  70. package/dist/shield/sarif.js +108 -0
  71. package/dist/shield/sarif.js.map +1 -0
  72. package/dist/shield/status.d.ts.map +1 -1
  73. package/dist/shield/status.js +19 -6
  74. package/dist/shield/status.js.map +1 -1
  75. package/dist/shield/types.d.ts +19 -1
  76. package/dist/shield/types.d.ts.map +1 -1
  77. package/dist/shield/types.js +2 -1
  78. package/dist/shield/types.js.map +1 -1
  79. package/package.json +5 -1
package/README.md CHANGED
@@ -52,6 +52,8 @@ Run `opena2a init` in any project directory to get an instant security assessmen
52
52
  MCP config found
53
53
  -----------------------------------------------
54
54
  Trust Score 30 / 100 [Grade: F]
55
+ Shield Posture 25 / 100 (CRITICAL)
56
+ Products 1/7 active
55
57
 
56
58
  Next Steps
57
59
  -----------------------------------------------
@@ -177,6 +179,228 @@ opena2a config contribute on # Enable community data sharing
177
179
  opena2a config llm on # Enable LLM-powered command matching
178
180
  ```
179
181
 
182
+ ## Shield: Unified Security Orchestration
183
+
184
+ Shield ties all OpenA2A products into a single security layer for AI coding assistants. It provides a tamper-evident event log, policy evaluation, runtime monitoring, session identification, integrity verification, and LLM-powered analysis.
185
+
186
+ ```bash
187
+ opena2a shield init # Full environment scan + policy generation
188
+ opena2a shield status # Product availability and integrity state
189
+ opena2a shield selfcheck # Run integrity checks across all subsystems
190
+ ```
191
+
192
+ ### How Shield protects your workstation
193
+
194
+ | Capability | What it does | Status |
195
+ |-----------|-------------|--------|
196
+ | **Credential scanning** | Detects hardcoded API keys (Anthropic, OpenAI, AWS, Google, GitHub) | Active |
197
+ | **Scope drift detection** | Finds API keys that silently access unintended services (DRIFT-001, DRIFT-002) | Active |
198
+ | **Tamper-evident event log** | SHA-256 hash-chained event log -- any modification breaks the chain | Active |
199
+ | **Policy evaluation** | Allow/deny rules for processes, credentials, network, filesystem, MCP servers | Active |
200
+ | **Session identification** | Detects which AI assistant is running (Claude Code, Cursor, Copilot, Windsurf) | Active |
201
+ | **Config integrity** | Signs config files and detects unauthorized modifications | Active |
202
+ | **ARP bridge** | Imports runtime protection events from HackMyAgent's ARP into Shield's log | Active |
203
+ | **Posture scoring** | 0-100 security score based on active products, policy, hooks, credentials | Active |
204
+ | **LLM intelligence** | AI-powered policy suggestions, anomaly explanations, incident triage | Active (opt-in) |
205
+ | **Integrity selfcheck** | Verifies policy, shell hooks, event chain, process, and artifact signatures | Active |
206
+ | **Lockdown mode** | Enters lockdown when integrity checks fail; requires explicit recovery | Active |
207
+ | **Adaptive baselines** | Learns per-agent behavior, tracks stability across sessions, suggests policies | Active |
208
+ | **Enforcement mode** | Shell hooks check policy in enforce mode and block denied commands | Active |
209
+ | **HTML posture report** | Interactive dark-theme HTML report with severity chart, filters, and agent activity | Active |
210
+
211
+ Shield operates in three modes:
212
+ - **Monitor** (default): Logs and surfaces security events for developer review.
213
+ - **Enforce**: Shell hooks call `opena2a shield evaluate` before each command. Denied commands are blocked with exit code 1.
214
+ - **Baseline learning**: Observes agent behavior across sessions and suggests policy rules when behavior stabilizes.
215
+
216
+ ### Subcommands
217
+
218
+ #### `opena2a shield init`
219
+
220
+ Full environment scan: detects project type, scans for credentials, discovers AI assistants, MCP servers, and OAuth sessions, generates a YAML policy file, installs shell hooks, and writes a genesis event to the tamper-evident log.
221
+
222
+ ```bash
223
+ opena2a shield init # Scan current directory
224
+ opena2a shield init --dir ./my-agent # Scan specific directory
225
+ opena2a shield init --format json # Machine-readable output
226
+ ```
227
+
228
+ #### `opena2a shield status`
229
+
230
+ Shows product availability, policy mode, shell integration, and integrity state.
231
+
232
+ ```bash
233
+ opena2a shield status
234
+ opena2a shield status --format json
235
+ ```
236
+
237
+ #### `opena2a shield log`
238
+
239
+ Query the tamper-evident event log with filters.
240
+
241
+ ```bash
242
+ opena2a shield log # Last 20 events
243
+ opena2a shield log --count 50 # Last 50 events
244
+ opena2a shield log --severity high # High+ severity only
245
+ opena2a shield log --source arp # ARP runtime events
246
+ opena2a shield log --agent claude-code # Events from Claude Code
247
+ opena2a shield log --since 7d # Last 7 days
248
+ opena2a shield log --format json # JSON output
249
+ ```
250
+
251
+ #### `opena2a shield selfcheck`
252
+
253
+ Runs five integrity checks: policy hash, shell hook content, event chain validity, process binary, and artifact signatures. Returns `healthy`, `degraded`, or `compromised` status.
254
+
255
+ ```bash
256
+ opena2a shield selfcheck
257
+ opena2a shield check # Alias
258
+ opena2a shield selfcheck --format json
259
+ ```
260
+
261
+ #### `opena2a shield policy`
262
+
263
+ Show the loaded security policy (mode, rule counts, agent overrides).
264
+
265
+ ```bash
266
+ opena2a shield policy
267
+ opena2a shield policy --format json
268
+ ```
269
+
270
+ #### `opena2a shield evaluate`
271
+
272
+ Evaluate an action against the loaded policy. Returns `ALLOWED`, `BLOCKED`, or `MONITORED`. In enforce mode, shell hooks call this before every command and block denied actions.
273
+
274
+ ```bash
275
+ opena2a shield evaluate --category processes --agent claude-code
276
+ opena2a shield evaluate "curl evil.com" # Evaluate a command string
277
+ opena2a shield evaluate --format json
278
+ ```
279
+
280
+ #### `opena2a shield monitor`
281
+
282
+ Import ARP (Agent Runtime Protection) events into Shield's hash-chained log and display runtime stats.
283
+
284
+ ```bash
285
+ opena2a shield monitor # Import events + show stats
286
+ opena2a shield monitor --agent cursor # Tag imported events
287
+ opena2a shield monitor --since 7d # Stats for last 7 days
288
+ opena2a shield monitor --format json
289
+ ```
290
+
291
+ #### `opena2a shield report`
292
+
293
+ Generate a security posture report from event data. Includes severity breakdown, agent activity, policy violations, and top actions. Supports interactive HTML output.
294
+
295
+ ```bash
296
+ opena2a shield report # Last 7 days (text)
297
+ opena2a shield report --report posture.html # Interactive HTML report
298
+ opena2a shield report --since 30d # Last 30 days
299
+ opena2a shield report --analyze # Include LLM narrative
300
+ opena2a shield report --format json
301
+ ```
302
+
303
+ #### `opena2a shield session`
304
+
305
+ Detect the current AI coding assistant session. Identifies Claude Code, Cursor, GitHub Copilot, Windsurf, Aider, and Continue.
306
+
307
+ ```bash
308
+ opena2a shield session
309
+ opena2a shield session --verbose # Show raw detection signals
310
+ opena2a shield session --format json
311
+ ```
312
+
313
+ #### `opena2a shield recover`
314
+
315
+ Exit lockdown mode after integrity failures. Optionally re-verify before lifting lockdown.
316
+
317
+ ```bash
318
+ opena2a shield recover # Exit lockdown
319
+ opena2a shield recover --verify # Verify first, then exit
320
+ ```
321
+
322
+ #### `opena2a shield suggest`
323
+
324
+ LLM-powered policy suggestion based on observed agent behavior. Requires LLM backend (enable with `opena2a config llm on`).
325
+
326
+ ```bash
327
+ opena2a shield suggest # Suggest policy from all events
328
+ opena2a shield suggest --agent cursor # For specific agent
329
+ opena2a shield suggest --format json
330
+ ```
331
+
332
+ #### `opena2a shield explain`
333
+
334
+ LLM-powered explanation of security events. Provides severity assessment, risk factors, and recommended actions.
335
+
336
+ ```bash
337
+ opena2a shield explain # Explain most recent event
338
+ opena2a shield explain --count 5 # Explain last 5 events
339
+ opena2a shield explain --severity high # High+ severity only
340
+ ```
341
+
342
+ #### `opena2a shield triage`
343
+
344
+ LLM-powered incident classification. Correlates multiple events and classifies as false-positive, suspicious, or confirmed-threat.
345
+
346
+ ```bash
347
+ opena2a shield triage # Triage high+ severity events
348
+ opena2a shield triage --severity medium # Include medium severity
349
+ opena2a shield triage --agent windsurf # For specific agent
350
+ ```
351
+
352
+ #### `opena2a shield baseline`
353
+
354
+ Manage per-agent behavioral baselines. Baselines track observed actions across sessions and compute stability scores to determine when behavior has converged enough to generate policy recommendations.
355
+
356
+ ```bash
357
+ opena2a shield baseline # List all baselines
358
+ opena2a shield baseline claude-code # Show detail for specific agent
359
+ opena2a shield baseline --format json
360
+ ```
361
+
362
+ Phases: **learning** (collecting observations) -> **stabilizing** (fewer new behaviors) -> **stable** (ready for policy generation). Stability is measured as the fraction of recent sessions with no previously-unseen behavior.
363
+
364
+ ### CI Integration
365
+
366
+ Shield includes a GitHub Actions workflow for automated security checks on every PR.
367
+
368
+ ```bash
369
+ # Copy the example workflow to your project
370
+ cp node_modules/opena2a-cli/examples/github-actions-shield.yml .github/workflows/shield.yml
371
+ ```
372
+
373
+ See `examples/github-actions-shield.yml` for a minimal copy-paste-ready workflow, or `.github/workflows/shield-check.yml` for the full implementation with PR comment integration.
374
+
375
+ ### Event Log Format
376
+
377
+ Shield maintains a tamper-evident event log at `~/.opena2a/shield/events.jsonl`. Each event is SHA-256 hash-chained to the previous event, starting from a genesis hash. Any modification to a past event breaks the chain and is detected by `selfcheck`.
378
+
379
+ ```
380
+ [2026-03-02T12:00:00Z] [HIGH] process.anomaly -> curl evil.com (monitored)
381
+ [2026-03-02T12:01:00Z] [CRITICAL] prompt.threat -> injection-attempt (blocked)
382
+ [2026-03-02T12:02:00Z] [INFO] process.spawn -> /usr/bin/ls (allowed)
383
+ ```
384
+
385
+ ### Quick Start
386
+
387
+ ```bash
388
+ # 1. Initialize Shield in your project
389
+ opena2a shield init
390
+
391
+ # 2. Check what AI assistants are running
392
+ opena2a shield session
393
+
394
+ # 3. View security events
395
+ opena2a shield log --severity medium
396
+
397
+ # 4. Generate a posture report
398
+ opena2a shield report
399
+
400
+ # 5. Run integrity verification
401
+ opena2a shield selfcheck
402
+ ```
403
+
180
404
  ## Smart Input Modes
181
405
 
182
406
  The CLI includes built-in intelligence for command discovery:
@@ -234,7 +458,7 @@ All commands support `--format json` and `--ci` flags for pipeline integration:
234
458
  |--------|------|----------|
235
459
  | Text | `--format text` (default) | Human-readable terminal output |
236
460
  | JSON | `--format json` | CI pipelines, programmatic consumption |
237
- | HTML | `--report <path>` | Interactive report with filtering (protect command) |
461
+ | HTML | `--report <path>` | Interactive report with filtering (protect and shield report) |
238
462
 
239
463
  ## Credential Patterns
240
464
 
@@ -0,0 +1,27 @@
1
+ /**
2
+ * ConfigGuard pre-commit hook integration.
3
+ *
4
+ * Installs/uninstalls a git pre-commit hook that runs `opena2a guard verify`
5
+ * before each commit, preventing commits when config files have been tampered.
6
+ */
7
+ export interface InstallResult {
8
+ installed: boolean;
9
+ path: string;
10
+ appended: boolean;
11
+ message: string;
12
+ }
13
+ declare function getHookScript(): string;
14
+ declare function installPreCommitHook(targetDir: string): InstallResult;
15
+ declare function uninstallPreCommitHook(targetDir: string): boolean;
16
+ declare function isHookInstalled(targetDir: string): boolean;
17
+ export declare function guardHook(action: string, targetDir: string): Promise<number>;
18
+ export declare const _internals: {
19
+ HOOK_MARKER: string;
20
+ HOOK_END_MARKER: string;
21
+ getHookScript: typeof getHookScript;
22
+ installPreCommitHook: typeof installPreCommitHook;
23
+ uninstallPreCommitHook: typeof uninstallPreCommitHook;
24
+ isHookInstalled: typeof isHookInstalled;
25
+ };
26
+ export {};
27
+ //# sourceMappingURL=guard-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-hooks.d.ts","sourceRoot":"","sources":["../../src/commands/guard-hooks.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AASD,iBAAS,aAAa,IAAI,MAAM,CAuB/B;AAID,iBAAS,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,CAyD9D;AAID,iBAAS,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAuB1D;AAID,iBAAS,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAKnD;AAID,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAsCjB;AAID,eAAO,MAAM,UAAU;;;;;;;CAOtB,CAAC"}
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ /**
3
+ * ConfigGuard pre-commit hook integration.
4
+ *
5
+ * Installs/uninstalls a git pre-commit hook that runs `opena2a guard verify`
6
+ * before each commit, preventing commits when config files have been tampered.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports._internals = void 0;
43
+ exports.guardHook = guardHook;
44
+ const fs = __importStar(require("node:fs"));
45
+ const path = __importStar(require("node:path"));
46
+ const colors_js_1 = require("../util/colors.js");
47
+ // --- Constants ---
48
+ const HOOK_MARKER = '# opena2a-guard pre-commit hook';
49
+ const HOOK_END_MARKER = '# end opena2a-guard pre-commit hook';
50
+ // --- Hook script ---
51
+ function getHookScript() {
52
+ return [
53
+ HOOK_MARKER,
54
+ '# Verify config file integrity before committing.',
55
+ '# Set SKIP_GUARD_VERIFY=1 to bypass in emergencies.',
56
+ 'if [ "$SKIP_GUARD_VERIFY" = "1" ]; then',
57
+ ' echo "[guard] SKIP_GUARD_VERIFY is set -- skipping config integrity check."',
58
+ 'else',
59
+ ' if [ -f ".opena2a/guard/signatures.json" ]; then',
60
+ ' npx opena2a guard verify --ci --format text',
61
+ ' GUARD_EXIT=$?',
62
+ ' if [ $GUARD_EXIT -ne 0 ]; then',
63
+ ' echo ""',
64
+ ' echo "[guard] Config integrity check FAILED. Commit blocked."',
65
+ ' echo "[guard] Run: opena2a guard sign to re-sign after intentional changes."',
66
+ ' echo "[guard] Set SKIP_GUARD_VERIFY=1 to bypass in emergencies."',
67
+ ' exit 1',
68
+ ' fi',
69
+ ' fi',
70
+ 'fi',
71
+ HOOK_END_MARKER,
72
+ '',
73
+ ].join('\n');
74
+ }
75
+ // --- Install ---
76
+ function installPreCommitHook(targetDir) {
77
+ const gitDir = path.join(targetDir, '.git');
78
+ if (!fs.existsSync(gitDir) || !fs.statSync(gitDir).isDirectory()) {
79
+ return {
80
+ installed: false,
81
+ path: '',
82
+ appended: false,
83
+ message: 'No .git directory found. Is this a git repository?',
84
+ };
85
+ }
86
+ const hooksDir = path.join(gitDir, 'hooks');
87
+ fs.mkdirSync(hooksDir, { recursive: true });
88
+ const hookPath = path.join(hooksDir, 'pre-commit');
89
+ const hookScript = getHookScript();
90
+ let appended = false;
91
+ if (fs.existsSync(hookPath)) {
92
+ const existing = fs.readFileSync(hookPath, 'utf-8');
93
+ // Already installed -- replace with latest version
94
+ if (existing.includes(HOOK_MARKER)) {
95
+ const before = existing.slice(0, existing.indexOf(HOOK_MARKER));
96
+ const afterMarker = existing.indexOf(HOOK_END_MARKER);
97
+ const after = afterMarker >= 0
98
+ ? existing.slice(afterMarker + HOOK_END_MARKER.length).replace(/^\n/, '')
99
+ : '';
100
+ const updated = before + hookScript + after;
101
+ fs.writeFileSync(hookPath, updated, 'utf-8');
102
+ fs.chmodSync(hookPath, 0o755);
103
+ return {
104
+ installed: true,
105
+ path: hookPath,
106
+ appended: false,
107
+ message: 'Pre-commit hook updated.',
108
+ };
109
+ }
110
+ // Foreign hook exists -- append ours
111
+ const separator = existing.endsWith('\n') ? '\n' : '\n\n';
112
+ fs.writeFileSync(hookPath, existing + separator + hookScript, 'utf-8');
113
+ appended = true;
114
+ }
115
+ else {
116
+ fs.writeFileSync(hookPath, '#!/bin/bash\n\n' + hookScript, 'utf-8');
117
+ }
118
+ fs.chmodSync(hookPath, 0o755);
119
+ return {
120
+ installed: true,
121
+ path: hookPath,
122
+ appended,
123
+ message: appended
124
+ ? 'Pre-commit hook appended to existing hook.'
125
+ : 'Pre-commit hook installed.',
126
+ };
127
+ }
128
+ // --- Uninstall ---
129
+ function uninstallPreCommitHook(targetDir) {
130
+ const hookPath = path.join(targetDir, '.git', 'hooks', 'pre-commit');
131
+ if (!fs.existsSync(hookPath))
132
+ return false;
133
+ const content = fs.readFileSync(hookPath, 'utf-8');
134
+ if (!content.includes(HOOK_MARKER))
135
+ return false;
136
+ const before = content.slice(0, content.indexOf(HOOK_MARKER));
137
+ const afterMarker = content.indexOf(HOOK_END_MARKER);
138
+ const after = afterMarker >= 0
139
+ ? content.slice(afterMarker + HOOK_END_MARKER.length).replace(/^\n/, '')
140
+ : '';
141
+ const remaining = (before + after).trim();
142
+ if (!remaining || remaining === '#!/bin/bash') {
143
+ // Nothing left -- remove the file entirely
144
+ fs.unlinkSync(hookPath);
145
+ }
146
+ else {
147
+ fs.writeFileSync(hookPath, remaining + '\n', 'utf-8');
148
+ }
149
+ return true;
150
+ }
151
+ // --- Status ---
152
+ function isHookInstalled(targetDir) {
153
+ const hookPath = path.join(targetDir, '.git', 'hooks', 'pre-commit');
154
+ if (!fs.existsSync(hookPath))
155
+ return false;
156
+ const content = fs.readFileSync(hookPath, 'utf-8');
157
+ return content.includes(HOOK_MARKER);
158
+ }
159
+ // --- CLI handler ---
160
+ async function guardHook(action, targetDir) {
161
+ switch (action) {
162
+ case 'install': {
163
+ const result = installPreCommitHook(targetDir);
164
+ if (!result.installed) {
165
+ process.stderr.write((0, colors_js_1.red)(result.message) + '\n');
166
+ return 1;
167
+ }
168
+ process.stdout.write((0, colors_js_1.green)(result.message) + '\n');
169
+ process.stdout.write((0, colors_js_1.dim)(` ${result.path}`) + '\n');
170
+ return 0;
171
+ }
172
+ case 'uninstall': {
173
+ const removed = uninstallPreCommitHook(targetDir);
174
+ if (removed) {
175
+ process.stdout.write((0, colors_js_1.green)('Pre-commit hook removed.') + '\n');
176
+ }
177
+ else {
178
+ process.stdout.write((0, colors_js_1.yellow)('No ConfigGuard hook found to remove.') + '\n');
179
+ }
180
+ return 0;
181
+ }
182
+ case 'status': {
183
+ const installed = isHookInstalled(targetDir);
184
+ if (installed) {
185
+ process.stdout.write((0, colors_js_1.bold)('ConfigGuard pre-commit hook: ') + (0, colors_js_1.green)('installed') + '\n');
186
+ }
187
+ else {
188
+ process.stdout.write((0, colors_js_1.bold)('ConfigGuard pre-commit hook: ') + (0, colors_js_1.dim)('not installed') + '\n');
189
+ }
190
+ return 0;
191
+ }
192
+ default:
193
+ process.stderr.write((0, colors_js_1.red)(`Unknown hook action: ${action}`) + '\n');
194
+ process.stderr.write('Usage: opena2a guard hook <install|uninstall|status>\n');
195
+ return 1;
196
+ }
197
+ }
198
+ // --- Testable internals ---
199
+ exports._internals = {
200
+ HOOK_MARKER,
201
+ HOOK_END_MARKER,
202
+ getHookScript,
203
+ installPreCommitHook,
204
+ uninstallPreCommitHook,
205
+ isHookInstalled,
206
+ };
207
+ //# sourceMappingURL=guard-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-hooks.js","sourceRoot":"","sources":["../../src/commands/guard-hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Guard policy: signing requirements and heartbeat disable on tamper.
3
+ *
4
+ * Loads policy from `.opena2a/guard/policy.json`, checks compliance
5
+ * against the signature store, and manages the heartbeat-disabled marker.
6
+ */
7
+ export interface GuardPolicy {
8
+ version: 1;
9
+ requiredFiles: string[];
10
+ blockOnUnsigned: boolean;
11
+ disableHeartbeatOnTamper: boolean;
12
+ autoRemediate: boolean;
13
+ }
14
+ export interface PolicyComplianceResult {
15
+ compliant: boolean;
16
+ requiredSigned: number;
17
+ requiredUnsigned: string[];
18
+ requiredTampered: string[];
19
+ requiredMissing: string[];
20
+ }
21
+ interface HeartbeatStatus {
22
+ disabled: boolean;
23
+ reason?: string;
24
+ disabledAt?: string;
25
+ }
26
+ declare function emitEvent(category: string, action: string, target: string, severity: 'info' | 'low' | 'medium' | 'high' | 'critical', outcome: 'allowed' | 'blocked' | 'monitored', detail: Record<string, unknown>): Promise<void>;
27
+ export declare function loadGuardPolicy(targetDir: string): GuardPolicy | null;
28
+ export declare function saveGuardPolicy(targetDir: string, policy: GuardPolicy): void;
29
+ export declare function generateDefaultPolicy(targetDir: string): GuardPolicy;
30
+ export declare function checkPolicyCompliance(targetDir: string, policy: GuardPolicy): PolicyComplianceResult;
31
+ export declare function disableHeartbeat(targetDir: string, reason: string): void;
32
+ export declare function isHeartbeatDisabled(targetDir: string): HeartbeatStatus;
33
+ export declare function enableHeartbeat(targetDir: string): void;
34
+ export declare function guardPolicy(targetDir: string, action: string, options: {
35
+ format?: 'text' | 'json';
36
+ }): Promise<number>;
37
+ export declare const _internals: {
38
+ loadGuardPolicy: typeof loadGuardPolicy;
39
+ saveGuardPolicy: typeof saveGuardPolicy;
40
+ generateDefaultPolicy: typeof generateDefaultPolicy;
41
+ checkPolicyCompliance: typeof checkPolicyCompliance;
42
+ disableHeartbeat: typeof disableHeartbeat;
43
+ isHeartbeatDisabled: typeof isHeartbeatDisabled;
44
+ enableHeartbeat: typeof enableHeartbeat;
45
+ guardPolicy: typeof guardPolicy;
46
+ emitEvent: typeof emitEvent;
47
+ GUARD_DIR: string;
48
+ POLICY_FILE: string;
49
+ HEARTBEAT_DISABLED_FILE: string;
50
+ SIGNATURES_FILE: string;
51
+ DEFAULT_CONFIG_FILES: string[];
52
+ };
53
+ export {};
54
+ //# sourceMappingURL=guard-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-policy.d.ts","sourceRoot":"","sources":["../../src/commands/guard-policy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;IACzB,wBAAwB,EAAE,OAAO,CAAC;IAClC,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,UAAU,eAAe;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAsBD,iBAAe,SAAS,CACtB,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAChD,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,EACzD,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,EAC5C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC,CAWf;AAID,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAIrE;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAI5E;AAID,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CASpE;AAID,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,sBAAsB,CA6BpG;AAID,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAKxE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,CAOtE;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAGvD;AAID,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAyD3H;AAID,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;CAKtB,CAAC"}