avorelo 0.3.3 → 0.3.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 +77 -95
- package/dist/avorelo.mjs +1164 -43
- package/package.json +7 -3
package/dist/avorelo.mjs
CHANGED
|
@@ -1702,8 +1702,8 @@ var init_generic = __esm({
|
|
|
1702
1702
|
const promptPath = join8(dir, ".avorelo", "prompt.md");
|
|
1703
1703
|
if (existsSync9(promptPath)) {
|
|
1704
1704
|
try {
|
|
1705
|
-
const { unlinkSync:
|
|
1706
|
-
|
|
1705
|
+
const { unlinkSync: unlinkSync6 } = __require("node:fs");
|
|
1706
|
+
unlinkSync6(promptPath);
|
|
1707
1707
|
return { removed: [promptPath], preserved: [] };
|
|
1708
1708
|
} catch {
|
|
1709
1709
|
return { removed: [], preserved: [promptPath] };
|
|
@@ -5510,6 +5510,15 @@ var init_plan_contract = __esm({
|
|
|
5510
5510
|
status: "implemented",
|
|
5511
5511
|
enforcementSurfaces: ["cli", "local-dashboard"],
|
|
5512
5512
|
privacyBoundary: "local_only"
|
|
5513
|
+
},
|
|
5514
|
+
{
|
|
5515
|
+
key: "artifact_guard_basic",
|
|
5516
|
+
pricingLabel: "Agent artifacts scanned for risks before AI touches the project",
|
|
5517
|
+
tier: "free",
|
|
5518
|
+
included: true,
|
|
5519
|
+
status: "implemented",
|
|
5520
|
+
enforcementSurfaces: ["cli"],
|
|
5521
|
+
privacyBoundary: "local_only"
|
|
5513
5522
|
}
|
|
5514
5523
|
];
|
|
5515
5524
|
PRO_CAPABILITIES = [
|
|
@@ -7325,8 +7334,8 @@ init_run();
|
|
|
7325
7334
|
init_work_contract();
|
|
7326
7335
|
init_receipts();
|
|
7327
7336
|
init_state_ledger();
|
|
7328
|
-
import { writeFileSync as
|
|
7329
|
-
import { join as
|
|
7337
|
+
import { writeFileSync as writeFileSync38, mkdirSync as mkdirSync40, existsSync as existsSync64, readFileSync as readFileSync50, appendFileSync as appendFileSync11, rmSync as rmSync3, unlinkSync as unlinkSync5 } from "node:fs";
|
|
7338
|
+
import { join as join66 } from "node:path";
|
|
7330
7339
|
|
|
7331
7340
|
// src/avorelo/capabilities/activation/index.ts
|
|
7332
7341
|
import { mkdtempSync, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11, rmSync as rmSync2, existsSync as existsSync24 } from "node:fs";
|
|
@@ -7449,7 +7458,7 @@ function runPreflight(targetDir) {
|
|
|
7449
7458
|
label: "npm cache directory accessible",
|
|
7450
7459
|
passed: false,
|
|
7451
7460
|
details: "cache path does not exist",
|
|
7452
|
-
recovery: isWindows ? 'Use a temporary cache. PowerShell: $env:npm_config_cache="$env:TEMP\\npm-cache-avorelo"; npx -y avorelo@latest activate
|
|
7461
|
+
recovery: isWindows ? 'Use a temporary cache. PowerShell: $env:npm_config_cache="$env:TEMP\\npm-cache-avorelo"; npx -y avorelo@latest activate --scope project-wide --claim <activation_claim>. cmd.exe: cmd /c "set npm_config_cache=%TEMP%\\npm-cache-avorelo && npx -y avorelo@latest activate --scope project-wide --claim <activation_claim>"' : "Use a temporary cache: npm_config_cache=$(mktemp -d) npx -y avorelo@latest activate --scope project-wide --claim <activation_claim>"
|
|
7453
7462
|
});
|
|
7454
7463
|
}
|
|
7455
7464
|
} catch {
|
|
@@ -7458,7 +7467,7 @@ function runPreflight(targetDir) {
|
|
|
7458
7467
|
label: "npm cache directory accessible",
|
|
7459
7468
|
passed: false,
|
|
7460
7469
|
details: "could not read npm cache config",
|
|
7461
|
-
recovery: isWindows ? 'Use a temporary cache. PowerShell: $env:npm_config_cache="$env:TEMP\\npm-cache-avorelo"; npx -y avorelo@latest activate
|
|
7470
|
+
recovery: isWindows ? 'Use a temporary cache. PowerShell: $env:npm_config_cache="$env:TEMP\\npm-cache-avorelo"; npx -y avorelo@latest activate --scope project-wide --claim <activation_claim>. cmd.exe: cmd /c "set npm_config_cache=%TEMP%\\npm-cache-avorelo && npx -y avorelo@latest activate --scope project-wide --claim <activation_claim>"' : "Use a temporary cache: npm_config_cache=$(mktemp -d) npx -y avorelo@latest activate --scope project-wide --claim <activation_claim>"
|
|
7462
7471
|
});
|
|
7463
7472
|
}
|
|
7464
7473
|
let tempOk = false;
|
|
@@ -7487,7 +7496,7 @@ function runPreflight(targetDir) {
|
|
|
7487
7496
|
label: "PowerShell execution policy",
|
|
7488
7497
|
passed: psOk,
|
|
7489
7498
|
details: `Policy: ${policy}`,
|
|
7490
|
-
recovery: blocked ? "PowerShell is blocking script execution. Use Command Prompt instead:\n cmd /c npx -y avorelo@latest activate" : void 0
|
|
7499
|
+
recovery: blocked ? "PowerShell is blocking script execution. Use Command Prompt instead:\n cmd /c npx -y avorelo@latest activate --scope project-wide --claim <activation_claim>" : void 0
|
|
7491
7500
|
});
|
|
7492
7501
|
} catch {
|
|
7493
7502
|
checks.push({ id: "powershell_execution_policy", label: "PowerShell execution policy", passed: true, details: "could not check (non-PowerShell shell)" });
|
|
@@ -7518,7 +7527,7 @@ function buildWindowsFallbackCommand() {
|
|
|
7518
7527
|
"mkdir %AVORELO_TEMP%",
|
|
7519
7528
|
"set npm_config_cache=%AVORELO_TEMP%\\npm-cache",
|
|
7520
7529
|
"cd /d %AVORELO_TEMP%",
|
|
7521
|
-
"npx -y avorelo@latest activate",
|
|
7530
|
+
"npx -y avorelo@latest activate --scope project-wide --claim <activation_claim>",
|
|
7522
7531
|
"npx -y avorelo@latest status"
|
|
7523
7532
|
].join("\n");
|
|
7524
7533
|
}
|
|
@@ -17608,6 +17617,1070 @@ async function sendDueTelemetry(dir, opts) {
|
|
|
17608
17617
|
}
|
|
17609
17618
|
}
|
|
17610
17619
|
|
|
17620
|
+
// src/avorelo/kernel/agent-artifact-guard/guard-handler.ts
|
|
17621
|
+
import { existsSync as existsSync63, readFileSync as readFileSync49 } from "node:fs";
|
|
17622
|
+
import { join as join65 } from "node:path";
|
|
17623
|
+
|
|
17624
|
+
// src/avorelo/kernel/agent-artifact-guard/index.ts
|
|
17625
|
+
import { join as join64 } from "node:path";
|
|
17626
|
+
import { readFileSync as readFileSync48 } from "node:fs";
|
|
17627
|
+
|
|
17628
|
+
// src/avorelo/kernel/agent-artifact-guard/artifact-discovery.ts
|
|
17629
|
+
import { readdirSync as readdirSync18, statSync as statSync13, existsSync as existsSync62 } from "node:fs";
|
|
17630
|
+
import { join as join61, relative as relative3, extname as extname2 } from "node:path";
|
|
17631
|
+
var SCANNED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
17632
|
+
".md",
|
|
17633
|
+
".js",
|
|
17634
|
+
".ts",
|
|
17635
|
+
".json",
|
|
17636
|
+
".yml",
|
|
17637
|
+
".yaml",
|
|
17638
|
+
".sh",
|
|
17639
|
+
".py",
|
|
17640
|
+
".txt",
|
|
17641
|
+
".toml"
|
|
17642
|
+
]);
|
|
17643
|
+
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
17644
|
+
"node_modules",
|
|
17645
|
+
"dist",
|
|
17646
|
+
".git",
|
|
17647
|
+
"build",
|
|
17648
|
+
"out",
|
|
17649
|
+
".next",
|
|
17650
|
+
"__pycache__"
|
|
17651
|
+
]);
|
|
17652
|
+
var ARTIFACT_PATTERNS = [
|
|
17653
|
+
{ kind: "claude-skill", paths: [".claude/skills"], recursive: true },
|
|
17654
|
+
{ kind: "claude-instructions", paths: ["CLAUDE.md", ".claude/CLAUDE.md"] },
|
|
17655
|
+
{ kind: "codex-instructions", paths: ["AGENTS.md", "CODEX.md", ".codex/CODEX.md"] },
|
|
17656
|
+
{ kind: "cursor-rules", paths: [".cursor/rules", ".cursorrules"], recursive: true },
|
|
17657
|
+
{ kind: "mcp-config", paths: [".claude/mcp.json", ".cursor/mcp.json", "mcp.json", ".mcp.json"] },
|
|
17658
|
+
{ kind: "github-actions", paths: [".github/workflows"], recursive: true },
|
|
17659
|
+
{ kind: "package-scripts", paths: ["package.json"] },
|
|
17660
|
+
{ kind: "hooks", paths: [".husky", ".git/hooks", ".claude/hooks"], recursive: true }
|
|
17661
|
+
];
|
|
17662
|
+
function walkDir2(dir, results) {
|
|
17663
|
+
let entries;
|
|
17664
|
+
try {
|
|
17665
|
+
entries = readdirSync18(dir);
|
|
17666
|
+
} catch {
|
|
17667
|
+
return;
|
|
17668
|
+
}
|
|
17669
|
+
for (const entry of entries) {
|
|
17670
|
+
if (IGNORED_DIRS.has(entry)) continue;
|
|
17671
|
+
const full = join61(dir, entry);
|
|
17672
|
+
let st;
|
|
17673
|
+
try {
|
|
17674
|
+
st = statSync13(full);
|
|
17675
|
+
} catch {
|
|
17676
|
+
continue;
|
|
17677
|
+
}
|
|
17678
|
+
if (st.isDirectory()) {
|
|
17679
|
+
walkDir2(full, results);
|
|
17680
|
+
} else if (st.isFile() && SCANNED_EXTENSIONS.has(extname2(full).toLowerCase())) {
|
|
17681
|
+
results.push(full);
|
|
17682
|
+
}
|
|
17683
|
+
}
|
|
17684
|
+
}
|
|
17685
|
+
function discoverArtifacts(projectRoot) {
|
|
17686
|
+
const artifacts = [];
|
|
17687
|
+
for (const pattern of ARTIFACT_PATTERNS) {
|
|
17688
|
+
for (const p of pattern.paths) {
|
|
17689
|
+
const fullPath = join61(projectRoot, p);
|
|
17690
|
+
if (!existsSync62(fullPath)) continue;
|
|
17691
|
+
let st;
|
|
17692
|
+
try {
|
|
17693
|
+
st = statSync13(fullPath);
|
|
17694
|
+
} catch {
|
|
17695
|
+
continue;
|
|
17696
|
+
}
|
|
17697
|
+
if (st.isFile()) {
|
|
17698
|
+
if (SCANNED_EXTENSIONS.has(extname2(fullPath).toLowerCase()) || fullPath.endsWith(".json")) {
|
|
17699
|
+
artifacts.push({
|
|
17700
|
+
kind: pattern.kind,
|
|
17701
|
+
path: relative3(projectRoot, fullPath)
|
|
17702
|
+
});
|
|
17703
|
+
}
|
|
17704
|
+
} else if (st.isDirectory() && pattern.recursive) {
|
|
17705
|
+
const files = [];
|
|
17706
|
+
walkDir2(fullPath, files);
|
|
17707
|
+
for (const f of files) {
|
|
17708
|
+
artifacts.push({
|
|
17709
|
+
kind: pattern.kind,
|
|
17710
|
+
path: relative3(projectRoot, f)
|
|
17711
|
+
});
|
|
17712
|
+
}
|
|
17713
|
+
}
|
|
17714
|
+
}
|
|
17715
|
+
}
|
|
17716
|
+
return artifacts;
|
|
17717
|
+
}
|
|
17718
|
+
function classifyArtifact(filePath) {
|
|
17719
|
+
const lower = filePath.toLowerCase().replace(/\\/g, "/");
|
|
17720
|
+
if (lower.includes(".claude/skills/")) return "claude-skill";
|
|
17721
|
+
if (lower.includes("claude.md")) return "claude-instructions";
|
|
17722
|
+
if (lower.includes("agents.md") || lower.includes("codex.md")) return "codex-instructions";
|
|
17723
|
+
if (lower.includes(".cursor/rules") || lower.includes(".cursorrules")) return "cursor-rules";
|
|
17724
|
+
if (lower.includes("mcp.json") || lower.includes("mcp.yaml")) return "mcp-config";
|
|
17725
|
+
if (lower.includes(".github/workflows/")) return "github-actions";
|
|
17726
|
+
if (lower === "package.json" || lower.endsWith("/package.json")) return "package-scripts";
|
|
17727
|
+
if (lower.includes(".husky/") || lower.includes("/hooks/")) return "hooks";
|
|
17728
|
+
if (lower.endsWith(".sh")) return "shell-script";
|
|
17729
|
+
return "unknown";
|
|
17730
|
+
}
|
|
17731
|
+
|
|
17732
|
+
// src/avorelo/kernel/agent-artifact-guard/rules.ts
|
|
17733
|
+
var RULES2 = [
|
|
17734
|
+
// --- Destructive commands ---
|
|
17735
|
+
{
|
|
17736
|
+
id: "destructive-rm-rf",
|
|
17737
|
+
title: "Recursive force delete",
|
|
17738
|
+
severity: "critical",
|
|
17739
|
+
category: "destructive-command",
|
|
17740
|
+
pattern: /rm\s+-(r|f|rf|fr)\s/,
|
|
17741
|
+
artifactKinds: "all",
|
|
17742
|
+
description: "Recursively and forcibly deletes files or directories. Can cause irreversible data loss.",
|
|
17743
|
+
recommendedAction: "Remove or scope the delete command. Use safe alternatives with explicit paths."
|
|
17744
|
+
},
|
|
17745
|
+
{
|
|
17746
|
+
id: "destructive-drop-table",
|
|
17747
|
+
title: "SQL DROP TABLE",
|
|
17748
|
+
severity: "critical",
|
|
17749
|
+
category: "destructive-command",
|
|
17750
|
+
pattern: /DROP\s+TABLE/i,
|
|
17751
|
+
artifactKinds: "all",
|
|
17752
|
+
description: "Drops a database table, causing irreversible data loss.",
|
|
17753
|
+
recommendedAction: "Remove DROP TABLE or add explicit safety guards."
|
|
17754
|
+
},
|
|
17755
|
+
{
|
|
17756
|
+
id: "destructive-format-disk",
|
|
17757
|
+
title: "Disk format command",
|
|
17758
|
+
severity: "critical",
|
|
17759
|
+
category: "destructive-command",
|
|
17760
|
+
pattern: /(?:mkfs|format\s+[A-Z]:)/i,
|
|
17761
|
+
artifactKinds: "all",
|
|
17762
|
+
description: "Formats a disk or partition, destroying all data.",
|
|
17763
|
+
recommendedAction: "Remove disk formatting commands from agent artifacts."
|
|
17764
|
+
},
|
|
17765
|
+
// --- Remote code execution ---
|
|
17766
|
+
{
|
|
17767
|
+
id: "rce-curl-pipe-bash",
|
|
17768
|
+
title: "Pipe curl to shell",
|
|
17769
|
+
severity: "critical",
|
|
17770
|
+
category: "remote-code-execution",
|
|
17771
|
+
pattern: /curl\s[^|]*\|\s*(?:bash|sh|zsh)/,
|
|
17772
|
+
artifactKinds: "all",
|
|
17773
|
+
description: "Downloads and immediately executes a remote script. Classic remote code execution vector.",
|
|
17774
|
+
recommendedAction: "Download the script first, review it, then execute separately."
|
|
17775
|
+
},
|
|
17776
|
+
{
|
|
17777
|
+
id: "rce-wget-pipe-sh",
|
|
17778
|
+
title: "Pipe wget to shell",
|
|
17779
|
+
severity: "critical",
|
|
17780
|
+
category: "remote-code-execution",
|
|
17781
|
+
pattern: /wget\s[^|]*\|\s*(?:bash|sh|zsh)/,
|
|
17782
|
+
artifactKinds: "all",
|
|
17783
|
+
description: "Downloads and immediately executes a remote script via wget.",
|
|
17784
|
+
recommendedAction: "Download the script first, review it, then execute separately."
|
|
17785
|
+
},
|
|
17786
|
+
{
|
|
17787
|
+
id: "rce-base64-decode-exec",
|
|
17788
|
+
title: "Base64 decode and execute",
|
|
17789
|
+
severity: "critical",
|
|
17790
|
+
category: "remote-code-execution",
|
|
17791
|
+
pattern: /base64\s+-d\s*\|\s*(?:bash|sh)/,
|
|
17792
|
+
artifactKinds: "all",
|
|
17793
|
+
description: "Decodes an obfuscated payload and pipes it to a shell.",
|
|
17794
|
+
recommendedAction: "Remove obfuscated execution. Use plain, reviewable scripts."
|
|
17795
|
+
},
|
|
17796
|
+
{
|
|
17797
|
+
id: "rce-reverse-shell",
|
|
17798
|
+
title: "Reverse or bind shell",
|
|
17799
|
+
severity: "critical",
|
|
17800
|
+
category: "remote-code-execution",
|
|
17801
|
+
pattern: /(?:nc\s+-e|\/dev\/tcp\/|ncat\s.*-e|socat\s.*exec)/,
|
|
17802
|
+
artifactKinds: "all",
|
|
17803
|
+
description: "Opens an interactive shell to a remote host. Post-exploitation technique.",
|
|
17804
|
+
recommendedAction: "Remove reverse/bind shell commands."
|
|
17805
|
+
},
|
|
17806
|
+
// --- Secret exposure ---
|
|
17807
|
+
{
|
|
17808
|
+
id: "secret-private-key",
|
|
17809
|
+
title: "Private key material",
|
|
17810
|
+
severity: "critical",
|
|
17811
|
+
category: "secret-exposure",
|
|
17812
|
+
pattern: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
|
|
17813
|
+
artifactKinds: "all",
|
|
17814
|
+
description: "Contains or references private key material.",
|
|
17815
|
+
recommendedAction: "Remove private key content. Use secret management instead."
|
|
17816
|
+
},
|
|
17817
|
+
{
|
|
17818
|
+
id: "secret-id-rsa",
|
|
17819
|
+
title: "SSH private key file reference",
|
|
17820
|
+
severity: "critical",
|
|
17821
|
+
category: "secret-exposure",
|
|
17822
|
+
pattern: /(?:id_rsa|id_ed25519|id_ecdsa)(?:\s|$|")/,
|
|
17823
|
+
artifactKinds: "all",
|
|
17824
|
+
description: "References an SSH private key file directly.",
|
|
17825
|
+
recommendedAction: "Use SSH agent or key references instead of direct file paths."
|
|
17826
|
+
},
|
|
17827
|
+
{
|
|
17828
|
+
id: "secret-ssh-dir",
|
|
17829
|
+
title: "SSH directory access",
|
|
17830
|
+
severity: "high",
|
|
17831
|
+
category: "secret-exposure",
|
|
17832
|
+
pattern: /~\/\.ssh\//,
|
|
17833
|
+
artifactKinds: "all",
|
|
17834
|
+
description: "Accesses the SSH directory containing private keys and host configurations.",
|
|
17835
|
+
recommendedAction: "Avoid direct SSH directory access in agent artifacts."
|
|
17836
|
+
},
|
|
17837
|
+
{
|
|
17838
|
+
id: "secret-env-file",
|
|
17839
|
+
title: ".env file reference",
|
|
17840
|
+
severity: "medium",
|
|
17841
|
+
category: "secret-exposure",
|
|
17842
|
+
pattern: /(?:\.env(?:\.\w+)?)\b/,
|
|
17843
|
+
artifactKinds: "all",
|
|
17844
|
+
description: "References a .env file which typically contains secrets and API keys.",
|
|
17845
|
+
recommendedAction: "Review whether the .env reference exposes sensitive values."
|
|
17846
|
+
},
|
|
17847
|
+
{
|
|
17848
|
+
id: "secret-process-env",
|
|
17849
|
+
title: "Environment variable access",
|
|
17850
|
+
severity: "low",
|
|
17851
|
+
category: "secret-exposure",
|
|
17852
|
+
pattern: /process\.env\b/,
|
|
17853
|
+
artifactKinds: "all",
|
|
17854
|
+
description: "Accesses environment variables which may contain secrets.",
|
|
17855
|
+
recommendedAction: "Verify that accessed environment variables do not contain sensitive values."
|
|
17856
|
+
},
|
|
17857
|
+
{
|
|
17858
|
+
id: "secret-api-key-pattern",
|
|
17859
|
+
title: "API key or token pattern",
|
|
17860
|
+
severity: "high",
|
|
17861
|
+
category: "secret-exposure",
|
|
17862
|
+
pattern: /(?:api[_-]?key|api[_-]?secret|auth[_-]?token)\s*[:=]/i,
|
|
17863
|
+
artifactKinds: "all",
|
|
17864
|
+
description: "Contains an API key or authentication token assignment.",
|
|
17865
|
+
recommendedAction: "Move credentials to environment variables or a secret manager."
|
|
17866
|
+
},
|
|
17867
|
+
// --- Privilege escalation ---
|
|
17868
|
+
{
|
|
17869
|
+
id: "priv-sudo",
|
|
17870
|
+
title: "Privilege escalation via sudo",
|
|
17871
|
+
severity: "high",
|
|
17872
|
+
category: "privilege-escalation",
|
|
17873
|
+
pattern: /\bsudo\b/,
|
|
17874
|
+
artifactKinds: "all",
|
|
17875
|
+
description: "Runs commands with elevated privileges.",
|
|
17876
|
+
recommendedAction: "Review whether elevated privileges are necessary. Scope to specific commands."
|
|
17877
|
+
},
|
|
17878
|
+
{
|
|
17879
|
+
id: "priv-chmod-exec",
|
|
17880
|
+
title: "Make file executable",
|
|
17881
|
+
severity: "medium",
|
|
17882
|
+
category: "privilege-escalation",
|
|
17883
|
+
pattern: /chmod\s+\+x/,
|
|
17884
|
+
artifactKinds: "all",
|
|
17885
|
+
description: "Marks a file as executable, often preceding execution of a downloaded binary.",
|
|
17886
|
+
recommendedAction: "Verify the file being made executable is trusted."
|
|
17887
|
+
},
|
|
17888
|
+
// --- Prompt injection / hidden instructions ---
|
|
17889
|
+
{
|
|
17890
|
+
id: "injection-prompt-override",
|
|
17891
|
+
title: "Prompt injection phrasing",
|
|
17892
|
+
severity: "high",
|
|
17893
|
+
category: "prompt-injection",
|
|
17894
|
+
pattern: /(?:ignore\s+(?:all\s+)?(?:previous|prior|above)\s+instructions|you\s+are\s+now\s+a|forget\s+(?:all\s+)?(?:your|previous)\s+instructions|disregard\s+(?:all\s+)?(?:previous|prior)\s+instructions|override\s+(?:all\s+)?(?:safety|security)\s+(?:rules|policies|instructions))/i,
|
|
17895
|
+
artifactKinds: "all",
|
|
17896
|
+
description: "Contains phrasing commonly used in prompt injection attacks to override agent instructions.",
|
|
17897
|
+
recommendedAction: "Remove instruction-override phrasing. Rewrite as direct task instructions."
|
|
17898
|
+
},
|
|
17899
|
+
{
|
|
17900
|
+
id: "injection-do-not-tell",
|
|
17901
|
+
title: "Concealment instruction",
|
|
17902
|
+
severity: "high",
|
|
17903
|
+
category: "hidden-instruction",
|
|
17904
|
+
pattern: /(?:do\s+not\s+tell\s+(?:the\s+)?user|hide\s+(?:this|these)\s+(?:changes?|from)|don'?t\s+(?:mention|reveal|show|disclose)\s+(?:this|these))/i,
|
|
17905
|
+
artifactKinds: "all",
|
|
17906
|
+
description: "Instructs the agent to conceal actions or information from the user.",
|
|
17907
|
+
recommendedAction: "Remove concealment instructions. Agent actions should be transparent."
|
|
17908
|
+
},
|
|
17909
|
+
{
|
|
17910
|
+
id: "injection-skip-tests",
|
|
17911
|
+
title: "Test bypass instruction",
|
|
17912
|
+
severity: "high",
|
|
17913
|
+
category: "hidden-instruction",
|
|
17914
|
+
pattern: /(?:skip\s+(?:all\s+)?tests?|don'?t\s+run\s+tests?|ignore\s+(?:test|lint|check)\s+(?:failures?|errors?|results?)|bypass\s+(?:ci|checks?|validation))/i,
|
|
17915
|
+
artifactKinds: "all",
|
|
17916
|
+
description: "Instructs the agent to skip testing, linting, or validation checks.",
|
|
17917
|
+
recommendedAction: "Remove test-bypass instructions. All changes should be validated."
|
|
17918
|
+
},
|
|
17919
|
+
{
|
|
17920
|
+
id: "injection-ignore-policy",
|
|
17921
|
+
title: "Policy bypass instruction",
|
|
17922
|
+
severity: "high",
|
|
17923
|
+
category: "hidden-instruction",
|
|
17924
|
+
pattern: /(?:ignore\s+(?:all\s+)?polic(?:y|ies)|always\s+approve|auto[_-]?approve\s+(?:all|everything)|skip\s+(?:review|approval))/i,
|
|
17925
|
+
artifactKinds: "all",
|
|
17926
|
+
description: "Instructs the agent to bypass approval policies or auto-approve actions.",
|
|
17927
|
+
recommendedAction: "Remove policy-bypass instructions. Approval workflows should be respected."
|
|
17928
|
+
},
|
|
17929
|
+
{
|
|
17930
|
+
id: "injection-push-to-main",
|
|
17931
|
+
title: "Direct push to main branch",
|
|
17932
|
+
severity: "high",
|
|
17933
|
+
category: "hidden-instruction",
|
|
17934
|
+
pattern: /(?:push\s+(?:directly\s+)?to\s+(?:main|master|production)|force[_-]?push|--force\s+(?:origin\s+)?(?:main|master))/i,
|
|
17935
|
+
artifactKinds: "all",
|
|
17936
|
+
description: "Instructs the agent to push directly to a protected branch or force-push.",
|
|
17937
|
+
recommendedAction: "Use pull requests and standard review workflows instead of direct pushes."
|
|
17938
|
+
},
|
|
17939
|
+
// --- Self-modifying ---
|
|
17940
|
+
{
|
|
17941
|
+
id: "selfmod-modify-own-skill",
|
|
17942
|
+
title: "Self-modifying skill instruction",
|
|
17943
|
+
severity: "high",
|
|
17944
|
+
category: "self-modifying",
|
|
17945
|
+
pattern: /(?:modify\s+(?:this|your\s+own)\s+(?:skill|instructions?|rules?|config)|write\s+(?:to|back\s+to)\s+(?:SKILL|CLAUDE|AGENTS)\.md|update\s+(?:your\s+own|this)\s+(?:skill|config)\s+file)/i,
|
|
17946
|
+
artifactKinds: "all",
|
|
17947
|
+
description: "Instructs the agent to modify its own skill, instructions, or configuration files.",
|
|
17948
|
+
recommendedAction: "Remove self-modification instructions. Agent artifacts should be immutable during execution."
|
|
17949
|
+
},
|
|
17950
|
+
// --- MCP tool poisoning ---
|
|
17951
|
+
{
|
|
17952
|
+
id: "mcp-hidden-instruction",
|
|
17953
|
+
title: "Hidden instruction in MCP tool description",
|
|
17954
|
+
severity: "high",
|
|
17955
|
+
category: "mcp-tool-poisoning",
|
|
17956
|
+
pattern: /(?:when\s+(?:the\s+)?user\s+(?:asks?|requests?)|secretly|silently\s+(?:send|post|upload|forward))/i,
|
|
17957
|
+
artifactKinds: ["mcp-config"],
|
|
17958
|
+
description: "MCP tool description contains instruction-like content that could manipulate agent behavior.",
|
|
17959
|
+
recommendedAction: "Review MCP tool descriptions for hidden instructions. Descriptions should only describe functionality."
|
|
17960
|
+
},
|
|
17961
|
+
// --- Package lifecycle risk ---
|
|
17962
|
+
{
|
|
17963
|
+
id: "pkg-lifecycle-exec",
|
|
17964
|
+
title: "Process execution in package lifecycle script",
|
|
17965
|
+
severity: "high",
|
|
17966
|
+
category: "package-lifecycle-risk",
|
|
17967
|
+
pattern: /(?:child_process|exec|spawn|execSync|spawnSync)\s*\(/,
|
|
17968
|
+
artifactKinds: ["package-scripts", "shell-script"],
|
|
17969
|
+
description: "Package lifecycle script or hook executes arbitrary processes.",
|
|
17970
|
+
recommendedAction: "Review lifecycle scripts for unexpected process execution."
|
|
17971
|
+
},
|
|
17972
|
+
{
|
|
17973
|
+
id: "pkg-lifecycle-network",
|
|
17974
|
+
title: "Network access in package lifecycle script",
|
|
17975
|
+
severity: "high",
|
|
17976
|
+
category: "package-lifecycle-risk",
|
|
17977
|
+
pattern: /(?:fetch|axios|https?\.(?:get|post|request)|XMLHttpRequest|node-fetch)\s*\(/,
|
|
17978
|
+
artifactKinds: ["package-scripts", "shell-script"],
|
|
17979
|
+
description: "Package lifecycle script makes network requests, potentially exfiltrating data.",
|
|
17980
|
+
recommendedAction: "Review lifecycle scripts for unexpected network access."
|
|
17981
|
+
},
|
|
17982
|
+
// --- Network exfiltration ---
|
|
17983
|
+
{
|
|
17984
|
+
id: "network-external-call",
|
|
17985
|
+
title: "External network request",
|
|
17986
|
+
severity: "medium",
|
|
17987
|
+
category: "network-exfiltration",
|
|
17988
|
+
pattern: /\b(?:curl|wget|fetch|axios|requests\.(?:get|post))\b/,
|
|
17989
|
+
artifactKinds: ["claude-skill", "claude-instructions", "cursor-rules", "codex-instructions", "hooks"],
|
|
17990
|
+
description: "Makes an external network request which could exfiltrate data or retrieve malicious payloads.",
|
|
17991
|
+
recommendedAction: "Verify the network call is necessary and targets a trusted endpoint."
|
|
17992
|
+
},
|
|
17993
|
+
// --- Eval / dynamic execution ---
|
|
17994
|
+
{
|
|
17995
|
+
id: "exec-eval",
|
|
17996
|
+
title: "Dynamic code execution",
|
|
17997
|
+
severity: "high",
|
|
17998
|
+
category: "remote-code-execution",
|
|
17999
|
+
pattern: /\b(?:eval|Invoke-Expression|Function\s*\()\s*\(/,
|
|
18000
|
+
artifactKinds: "all",
|
|
18001
|
+
description: "Executes a string as code, enabling injection and obfuscation attacks.",
|
|
18002
|
+
recommendedAction: "Remove eval/dynamic execution. Use explicit, reviewable code paths."
|
|
18003
|
+
},
|
|
18004
|
+
{
|
|
18005
|
+
id: "exec-child-process",
|
|
18006
|
+
title: "Node child_process import",
|
|
18007
|
+
severity: "high",
|
|
18008
|
+
category: "remote-code-execution",
|
|
18009
|
+
pattern: /require\s*\(\s*['"]child_process['"]\s*\)|from\s+['"]child_process['"]/,
|
|
18010
|
+
artifactKinds: "all",
|
|
18011
|
+
description: "Imports Node.js child_process module for arbitrary command execution.",
|
|
18012
|
+
recommendedAction: "Review whether child_process usage is necessary and scoped."
|
|
18013
|
+
},
|
|
18014
|
+
// --- Filesystem access ---
|
|
18015
|
+
{
|
|
18016
|
+
id: "fs-broad-access",
|
|
18017
|
+
title: "Broad filesystem access",
|
|
18018
|
+
severity: "medium",
|
|
18019
|
+
category: "filesystem-access",
|
|
18020
|
+
pattern: /(?:fs\.(?:readdir|rmdir|unlink)Sync|glob\s*\(\s*['"]\/|readdir\s*\(\s*['"]\/)/,
|
|
18021
|
+
artifactKinds: "all",
|
|
18022
|
+
description: "Performs broad filesystem operations that could access or delete files outside the project.",
|
|
18023
|
+
recommendedAction: "Scope filesystem operations to the project directory."
|
|
18024
|
+
},
|
|
18025
|
+
// --- Deploy / publish ---
|
|
18026
|
+
{
|
|
18027
|
+
id: "deploy-npm-publish",
|
|
18028
|
+
title: "npm publish command",
|
|
18029
|
+
severity: "high",
|
|
18030
|
+
category: "deploy-publish",
|
|
18031
|
+
pattern: /npm\s+publish/,
|
|
18032
|
+
artifactKinds: "all",
|
|
18033
|
+
description: "Publishes a package to npm, which is an irreversible public action.",
|
|
18034
|
+
recommendedAction: "Remove automatic publish commands. Publishing should be a deliberate human action."
|
|
18035
|
+
},
|
|
18036
|
+
{
|
|
18037
|
+
id: "deploy-dangerous-action",
|
|
18038
|
+
title: "Dangerous deploy/publish in CI",
|
|
18039
|
+
severity: "high",
|
|
18040
|
+
category: "deploy-publish",
|
|
18041
|
+
pattern: /(?:deploy\s+(?:--)?(?:prod|production)|push\s+(?:to\s+)?(?:prod|production|live))/i,
|
|
18042
|
+
artifactKinds: ["github-actions", "shell-script", "hooks"],
|
|
18043
|
+
description: "Deploys directly to production without explicit approval gates.",
|
|
18044
|
+
recommendedAction: "Add approval gates before production deployments."
|
|
18045
|
+
}
|
|
18046
|
+
];
|
|
18047
|
+
function getRule(id) {
|
|
18048
|
+
return RULES2.find((r2) => r2.id === id);
|
|
18049
|
+
}
|
|
18050
|
+
|
|
18051
|
+
// src/avorelo/kernel/agent-artifact-guard/scanner.ts
|
|
18052
|
+
var MAX_PREVIEW_LENGTH = 80;
|
|
18053
|
+
function safeMatchPreview(match) {
|
|
18054
|
+
let preview = match.trim();
|
|
18055
|
+
if (preview.length > MAX_PREVIEW_LENGTH) {
|
|
18056
|
+
preview = preview.slice(0, MAX_PREVIEW_LENGTH) + "...";
|
|
18057
|
+
}
|
|
18058
|
+
preview = preview.replace(
|
|
18059
|
+
/(?:key|secret|token|password|passwd|pwd)\s*[:=]\s*['"]?[a-zA-Z0-9_\-./+=]{8,}/gi,
|
|
18060
|
+
(m) => {
|
|
18061
|
+
const eqIdx = m.search(/[:=]/);
|
|
18062
|
+
if (eqIdx >= 0) return m.slice(0, eqIdx + 1) + " [REDACTED]";
|
|
18063
|
+
return "[REDACTED]";
|
|
18064
|
+
}
|
|
18065
|
+
);
|
|
18066
|
+
return preview;
|
|
18067
|
+
}
|
|
18068
|
+
function rulesForArtifact(kind) {
|
|
18069
|
+
return RULES2.filter(
|
|
18070
|
+
(r2) => r2.artifactKinds === "all" || r2.artifactKinds.includes(kind)
|
|
18071
|
+
);
|
|
18072
|
+
}
|
|
18073
|
+
function scanContent2(content, filePath, artifactKind) {
|
|
18074
|
+
const kind = artifactKind ?? classifyArtifact(filePath);
|
|
18075
|
+
const applicable = rulesForArtifact(kind);
|
|
18076
|
+
const findings = [];
|
|
18077
|
+
const lines = content.split("\n");
|
|
18078
|
+
for (const rule of applicable) {
|
|
18079
|
+
if (rule.multiline) continue;
|
|
18080
|
+
for (let i = 0; i < lines.length; i++) {
|
|
18081
|
+
const match = rule.pattern.exec(lines[i]);
|
|
18082
|
+
if (match) {
|
|
18083
|
+
findings.push({
|
|
18084
|
+
ruleId: rule.id,
|
|
18085
|
+
title: rule.title,
|
|
18086
|
+
severity: rule.severity,
|
|
18087
|
+
category: rule.category,
|
|
18088
|
+
file: filePath,
|
|
18089
|
+
line: i + 1,
|
|
18090
|
+
matchPreview: safeMatchPreview(match[0]),
|
|
18091
|
+
description: rule.description,
|
|
18092
|
+
recommendedAction: rule.recommendedAction,
|
|
18093
|
+
artifactKind: kind
|
|
18094
|
+
});
|
|
18095
|
+
}
|
|
18096
|
+
}
|
|
18097
|
+
}
|
|
18098
|
+
for (const rule of applicable) {
|
|
18099
|
+
if (!rule.multiline) continue;
|
|
18100
|
+
const match = rule.pattern.exec(content);
|
|
18101
|
+
if (match) {
|
|
18102
|
+
const lineNumber = content.slice(0, match.index).split("\n").length;
|
|
18103
|
+
findings.push({
|
|
18104
|
+
ruleId: rule.id,
|
|
18105
|
+
title: rule.title,
|
|
18106
|
+
severity: rule.severity,
|
|
18107
|
+
category: rule.category,
|
|
18108
|
+
file: filePath,
|
|
18109
|
+
line: lineNumber,
|
|
18110
|
+
matchPreview: safeMatchPreview(match[0]),
|
|
18111
|
+
description: rule.description,
|
|
18112
|
+
recommendedAction: rule.recommendedAction,
|
|
18113
|
+
artifactKind: kind
|
|
18114
|
+
});
|
|
18115
|
+
}
|
|
18116
|
+
}
|
|
18117
|
+
findings.sort((a, b) => a.line - b.line);
|
|
18118
|
+
return findings;
|
|
18119
|
+
}
|
|
18120
|
+
|
|
18121
|
+
// src/avorelo/kernel/agent-artifact-guard/scorer.ts
|
|
18122
|
+
var SEVERITY_WEIGHTS = {
|
|
18123
|
+
low: 1,
|
|
18124
|
+
medium: 3,
|
|
18125
|
+
high: 6,
|
|
18126
|
+
critical: 10
|
|
18127
|
+
};
|
|
18128
|
+
function countBySeverity(findings) {
|
|
18129
|
+
const counts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
18130
|
+
for (const f of findings) {
|
|
18131
|
+
counts[f.severity]++;
|
|
18132
|
+
}
|
|
18133
|
+
return counts;
|
|
18134
|
+
}
|
|
18135
|
+
function computeRiskScore(counts) {
|
|
18136
|
+
if (counts.critical > 0) return 10;
|
|
18137
|
+
const weighted = counts.high * SEVERITY_WEIGHTS.high + counts.medium * SEVERITY_WEIGHTS.medium + counts.low * SEVERITY_WEIGHTS.low;
|
|
18138
|
+
if (weighted === 0) return 0;
|
|
18139
|
+
const score = Math.min(9.9, Math.log2(weighted + 1) * 1.5);
|
|
18140
|
+
return Math.round(score * 10) / 10;
|
|
18141
|
+
}
|
|
18142
|
+
|
|
18143
|
+
// src/avorelo/kernel/agent-artifact-guard/policy.ts
|
|
18144
|
+
function evaluatePolicy2(counts, riskScore) {
|
|
18145
|
+
let findingOutcome = "pass";
|
|
18146
|
+
if (counts.critical > 0) findingOutcome = "blocked";
|
|
18147
|
+
else if (counts.high > 0) findingOutcome = "requires_approval";
|
|
18148
|
+
else if (counts.medium > 0) findingOutcome = "warn";
|
|
18149
|
+
let scoreOutcome = "pass";
|
|
18150
|
+
if (riskScore >= 9) scoreOutcome = "blocked";
|
|
18151
|
+
else if (riskScore >= 6) scoreOutcome = "requires_approval";
|
|
18152
|
+
else if (riskScore >= 3) scoreOutcome = "warn";
|
|
18153
|
+
const ORDER = ["pass", "warn", "requires_approval", "blocked"];
|
|
18154
|
+
const findingRank = ORDER.indexOf(findingOutcome);
|
|
18155
|
+
const scoreRank = ORDER.indexOf(scoreOutcome);
|
|
18156
|
+
return ORDER[Math.max(findingRank, scoreRank)];
|
|
18157
|
+
}
|
|
18158
|
+
function computeNextBestAction(outcome, counts) {
|
|
18159
|
+
switch (outcome) {
|
|
18160
|
+
case "blocked":
|
|
18161
|
+
return `${counts.critical} critical finding(s) must be resolved before proceeding.`;
|
|
18162
|
+
case "requires_approval":
|
|
18163
|
+
return `${counts.high} high-severity finding(s) require review and approval before proceeding.`;
|
|
18164
|
+
case "warn":
|
|
18165
|
+
return `${counts.medium} medium-severity finding(s) detected. Review recommended.`;
|
|
18166
|
+
case "pass":
|
|
18167
|
+
return "No blocking findings. Artifacts are ready for use.";
|
|
18168
|
+
}
|
|
18169
|
+
}
|
|
18170
|
+
|
|
18171
|
+
// src/avorelo/kernel/agent-artifact-guard/receipt.ts
|
|
18172
|
+
import { createHash as createHash18 } from "node:crypto";
|
|
18173
|
+
function createReceipt(result3) {
|
|
18174
|
+
return {
|
|
18175
|
+
receiptType: "agent-artifact-guard-v1",
|
|
18176
|
+
scannedAt: result3.scannedAt,
|
|
18177
|
+
projectRootHash: createHash18("sha256").update(result3.projectRoot).digest("hex"),
|
|
18178
|
+
filesScanned: result3.filesScanned,
|
|
18179
|
+
artifacts: result3.artifacts.map((a) => ({ kind: a.kind, path: a.path })),
|
|
18180
|
+
findingSummary: {
|
|
18181
|
+
total: result3.findings.length,
|
|
18182
|
+
...result3.counts
|
|
18183
|
+
},
|
|
18184
|
+
findings: result3.findings.map((f) => ({
|
|
18185
|
+
ruleId: f.ruleId,
|
|
18186
|
+
severity: f.severity,
|
|
18187
|
+
file: f.file,
|
|
18188
|
+
line: f.line,
|
|
18189
|
+
title: f.title,
|
|
18190
|
+
matchPreview: f.matchPreview,
|
|
18191
|
+
recommendedAction: f.recommendedAction
|
|
18192
|
+
})),
|
|
18193
|
+
riskScore: result3.riskScore,
|
|
18194
|
+
policyOutcome: result3.policyOutcome,
|
|
18195
|
+
nextBestAction: result3.nextBestAction,
|
|
18196
|
+
containsRawPrompt: false,
|
|
18197
|
+
containsRawSource: false,
|
|
18198
|
+
containsRawSecret: false,
|
|
18199
|
+
contentStored: false
|
|
18200
|
+
};
|
|
18201
|
+
}
|
|
18202
|
+
|
|
18203
|
+
// src/avorelo/kernel/agent-artifact-guard/reporter.ts
|
|
18204
|
+
var SEVERITY_BADGE = {
|
|
18205
|
+
critical: "[CRITICAL]",
|
|
18206
|
+
high: "[HIGH]",
|
|
18207
|
+
medium: "[MEDIUM]",
|
|
18208
|
+
low: "[LOW]"
|
|
18209
|
+
};
|
|
18210
|
+
var OUTCOME_LABEL = {
|
|
18211
|
+
pass: "PASS",
|
|
18212
|
+
warn: "WARN",
|
|
18213
|
+
requires_approval: "REQUIRES APPROVAL",
|
|
18214
|
+
blocked: "BLOCKED"
|
|
18215
|
+
};
|
|
18216
|
+
function riskBar(score) {
|
|
18217
|
+
const filled = Math.round(score / 10 * 20);
|
|
18218
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(20 - filled);
|
|
18219
|
+
}
|
|
18220
|
+
function groupByFile(findings) {
|
|
18221
|
+
const map = /* @__PURE__ */ new Map();
|
|
18222
|
+
for (const f of findings) {
|
|
18223
|
+
const group = map.get(f.file) ?? [];
|
|
18224
|
+
group.push(f);
|
|
18225
|
+
map.set(f.file, group);
|
|
18226
|
+
}
|
|
18227
|
+
return map;
|
|
18228
|
+
}
|
|
18229
|
+
function renderReport(result3) {
|
|
18230
|
+
const lines = [];
|
|
18231
|
+
lines.push(`Artifact Guard Scan`);
|
|
18232
|
+
lines.push(`Path: ${result3.projectRoot}`);
|
|
18233
|
+
lines.push(`Files scanned: ${result3.filesScanned}`);
|
|
18234
|
+
lines.push(`Artifacts found: ${result3.artifacts.length}`);
|
|
18235
|
+
lines.push(`Findings: ${result3.findings.length}`);
|
|
18236
|
+
lines.push("");
|
|
18237
|
+
if (result3.findings.length > 0) {
|
|
18238
|
+
const grouped = groupByFile(result3.findings);
|
|
18239
|
+
for (const [file, findings] of grouped) {
|
|
18240
|
+
lines.push(` ${file}`);
|
|
18241
|
+
for (const f of findings) {
|
|
18242
|
+
lines.push(` L${f.line} ${SEVERITY_BADGE[f.severity]} ${f.title} (${f.ruleId})`);
|
|
18243
|
+
lines.push(` ${f.matchPreview}`);
|
|
18244
|
+
lines.push(` ${f.description}`);
|
|
18245
|
+
}
|
|
18246
|
+
lines.push("");
|
|
18247
|
+
}
|
|
18248
|
+
}
|
|
18249
|
+
lines.push(`Risk: ${riskBar(result3.riskScore)} ${result3.riskScore}/10`);
|
|
18250
|
+
lines.push(`Outcome: ${OUTCOME_LABEL[result3.policyOutcome]}`);
|
|
18251
|
+
lines.push("");
|
|
18252
|
+
lines.push(`Next: ${result3.nextBestAction}`);
|
|
18253
|
+
return lines.join("\n");
|
|
18254
|
+
}
|
|
18255
|
+
function renderSummary(result3) {
|
|
18256
|
+
const parts = [];
|
|
18257
|
+
parts.push(`Artifact Guard: ${OUTCOME_LABEL[result3.policyOutcome]}`);
|
|
18258
|
+
if (result3.findings.length > 0) {
|
|
18259
|
+
const counts = [];
|
|
18260
|
+
if (result3.counts.critical > 0) counts.push(`${result3.counts.critical} critical`);
|
|
18261
|
+
if (result3.counts.high > 0) counts.push(`${result3.counts.high} high`);
|
|
18262
|
+
if (result3.counts.medium > 0) counts.push(`${result3.counts.medium} medium`);
|
|
18263
|
+
if (result3.counts.low > 0) counts.push(`${result3.counts.low} low`);
|
|
18264
|
+
parts.push(`(${counts.join(", ")})`);
|
|
18265
|
+
} else {
|
|
18266
|
+
parts.push("(clean)");
|
|
18267
|
+
}
|
|
18268
|
+
return parts.join(" ");
|
|
18269
|
+
}
|
|
18270
|
+
function toJson(result3) {
|
|
18271
|
+
return JSON.stringify(result3, null, 2);
|
|
18272
|
+
}
|
|
18273
|
+
|
|
18274
|
+
// src/avorelo/kernel/agent-artifact-guard/allowlist.ts
|
|
18275
|
+
import { readFileSync as readFileSync46 } from "node:fs";
|
|
18276
|
+
import { join as join62 } from "node:path";
|
|
18277
|
+
function loadAllowlist(projectRoot) {
|
|
18278
|
+
const filePath = join62(projectRoot, ".avorelo", "artifact-guard", "allowlist.json");
|
|
18279
|
+
try {
|
|
18280
|
+
const raw = readFileSync46(filePath, "utf-8");
|
|
18281
|
+
const parsed = JSON.parse(raw);
|
|
18282
|
+
if (!Array.isArray(parsed.entries)) return [];
|
|
18283
|
+
return parsed.entries.filter((e) => typeof e.reason === "string" && e.reason.length > 0);
|
|
18284
|
+
} catch {
|
|
18285
|
+
return [];
|
|
18286
|
+
}
|
|
18287
|
+
}
|
|
18288
|
+
function isAllowlisted(finding, allowlist) {
|
|
18289
|
+
if (finding.severity === "critical") return false;
|
|
18290
|
+
for (const entry of allowlist) {
|
|
18291
|
+
if (!entry.reason) continue;
|
|
18292
|
+
const ruleMatch = !entry.ruleId || entry.ruleId === finding.ruleId;
|
|
18293
|
+
const fileMatch = !entry.file || finding.file === entry.file || finding.file.startsWith(entry.file);
|
|
18294
|
+
if (ruleMatch && fileMatch) return true;
|
|
18295
|
+
}
|
|
18296
|
+
return false;
|
|
18297
|
+
}
|
|
18298
|
+
|
|
18299
|
+
// src/avorelo/kernel/agent-artifact-guard/receipt-store.ts
|
|
18300
|
+
import { mkdirSync as mkdirSync39, writeFileSync as writeFileSync37, readdirSync as readdirSync19, readFileSync as readFileSync47, unlinkSync as unlinkSync4 } from "node:fs";
|
|
18301
|
+
import { join as join63 } from "node:path";
|
|
18302
|
+
var RECEIPT_DIR = "artifact-guard";
|
|
18303
|
+
var MAX_STORED_RECEIPTS = 50;
|
|
18304
|
+
function receiptDir(projectRoot) {
|
|
18305
|
+
return join63(projectRoot, ".avorelo", RECEIPT_DIR);
|
|
18306
|
+
}
|
|
18307
|
+
function receiptFilename(receipt) {
|
|
18308
|
+
const ts = receipt.scannedAt.replace(/[:.]/g, "-");
|
|
18309
|
+
return `receipt-${ts}.json`;
|
|
18310
|
+
}
|
|
18311
|
+
function storeReceipt(projectRoot, receipt) {
|
|
18312
|
+
const dir = receiptDir(projectRoot);
|
|
18313
|
+
mkdirSync39(dir, { recursive: true });
|
|
18314
|
+
const filename = receiptFilename(receipt);
|
|
18315
|
+
const filePath = join63(dir, filename);
|
|
18316
|
+
writeFileSync37(filePath, JSON.stringify(receipt, null, 2), "utf-8");
|
|
18317
|
+
pruneOldReceipts(dir);
|
|
18318
|
+
return filePath;
|
|
18319
|
+
}
|
|
18320
|
+
function loadLatestReceipt(projectRoot) {
|
|
18321
|
+
const dir = receiptDir(projectRoot);
|
|
18322
|
+
let files;
|
|
18323
|
+
try {
|
|
18324
|
+
files = readdirSync19(dir).filter((f) => f.startsWith("receipt-") && f.endsWith(".json"));
|
|
18325
|
+
} catch {
|
|
18326
|
+
return null;
|
|
18327
|
+
}
|
|
18328
|
+
if (files.length === 0) return null;
|
|
18329
|
+
files.sort();
|
|
18330
|
+
const latest = files[files.length - 1];
|
|
18331
|
+
try {
|
|
18332
|
+
const raw = readFileSync47(join63(dir, latest), "utf-8");
|
|
18333
|
+
return JSON.parse(raw);
|
|
18334
|
+
} catch {
|
|
18335
|
+
return null;
|
|
18336
|
+
}
|
|
18337
|
+
}
|
|
18338
|
+
function pruneOldReceipts(dir) {
|
|
18339
|
+
try {
|
|
18340
|
+
const files = readdirSync19(dir).filter((f) => f.startsWith("receipt-") && f.endsWith(".json")).sort();
|
|
18341
|
+
while (files.length > MAX_STORED_RECEIPTS) {
|
|
18342
|
+
const oldest = files.shift();
|
|
18343
|
+
try {
|
|
18344
|
+
unlinkSync4(join63(dir, oldest));
|
|
18345
|
+
} catch {
|
|
18346
|
+
break;
|
|
18347
|
+
}
|
|
18348
|
+
}
|
|
18349
|
+
} catch {
|
|
18350
|
+
}
|
|
18351
|
+
}
|
|
18352
|
+
|
|
18353
|
+
// src/avorelo/kernel/agent-artifact-guard/index.ts
|
|
18354
|
+
function scan(projectRoot, options) {
|
|
18355
|
+
const artifacts = discoverArtifacts(projectRoot);
|
|
18356
|
+
const allFindings = [];
|
|
18357
|
+
for (const artifact of artifacts) {
|
|
18358
|
+
const fullPath = join64(projectRoot, artifact.path);
|
|
18359
|
+
let content;
|
|
18360
|
+
try {
|
|
18361
|
+
content = readFileSync48(fullPath, "utf-8");
|
|
18362
|
+
} catch {
|
|
18363
|
+
continue;
|
|
18364
|
+
}
|
|
18365
|
+
const findings = scanContent2(content, artifact.path, artifact.kind);
|
|
18366
|
+
allFindings.push(...findings);
|
|
18367
|
+
}
|
|
18368
|
+
let effectiveFindings = allFindings;
|
|
18369
|
+
let allowlistedCount = 0;
|
|
18370
|
+
if (options?.applyAllowlist !== false) {
|
|
18371
|
+
const allowlist = loadAllowlist(projectRoot);
|
|
18372
|
+
if (allowlist.length > 0) {
|
|
18373
|
+
effectiveFindings = [];
|
|
18374
|
+
for (const f of allFindings) {
|
|
18375
|
+
if (isAllowlisted(f, allowlist)) {
|
|
18376
|
+
allowlistedCount++;
|
|
18377
|
+
} else {
|
|
18378
|
+
effectiveFindings.push(f);
|
|
18379
|
+
}
|
|
18380
|
+
}
|
|
18381
|
+
}
|
|
18382
|
+
}
|
|
18383
|
+
const counts = countBySeverity(effectiveFindings);
|
|
18384
|
+
const riskScore = computeRiskScore(counts);
|
|
18385
|
+
const policyOutcome = evaluatePolicy2(counts, riskScore);
|
|
18386
|
+
let nextBestAction = computeNextBestAction(policyOutcome, counts);
|
|
18387
|
+
if (allowlistedCount > 0) {
|
|
18388
|
+
nextBestAction += ` (${allowlistedCount} finding(s) allowlisted)`;
|
|
18389
|
+
}
|
|
18390
|
+
return {
|
|
18391
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18392
|
+
projectRoot,
|
|
18393
|
+
filesScanned: artifacts.length,
|
|
18394
|
+
artifacts,
|
|
18395
|
+
findings: effectiveFindings,
|
|
18396
|
+
counts,
|
|
18397
|
+
riskScore,
|
|
18398
|
+
policyOutcome,
|
|
18399
|
+
nextBestAction
|
|
18400
|
+
};
|
|
18401
|
+
}
|
|
18402
|
+
|
|
18403
|
+
// src/avorelo/kernel/agent-artifact-guard/guard-handler.ts
|
|
18404
|
+
function checkActivation(dir) {
|
|
18405
|
+
const configPath = join65(dir, ".avorelo", "model-routing", "config.json");
|
|
18406
|
+
try {
|
|
18407
|
+
if (existsSync63(configPath)) {
|
|
18408
|
+
const raw = JSON.parse(readFileSync49(configPath, "utf-8"));
|
|
18409
|
+
return {
|
|
18410
|
+
activated: true,
|
|
18411
|
+
routingMode: raw.mode ?? "seamless",
|
|
18412
|
+
localFirst: raw.localFirst ?? true,
|
|
18413
|
+
registryLoaded: raw.registryLoaded ?? false,
|
|
18414
|
+
cloudRoutingEnabled: raw.cloudRoutingEnabled ?? false,
|
|
18415
|
+
defaultPolicy: raw.defaultPolicy ?? "safety_proof_privacy_before_cost"
|
|
18416
|
+
};
|
|
18417
|
+
}
|
|
18418
|
+
} catch {
|
|
18419
|
+
}
|
|
18420
|
+
return {
|
|
18421
|
+
activated: false,
|
|
18422
|
+
routingMode: "seamless",
|
|
18423
|
+
localFirst: true,
|
|
18424
|
+
registryLoaded: false,
|
|
18425
|
+
cloudRoutingEnabled: false,
|
|
18426
|
+
defaultPolicy: "safety_proof_privacy_before_cost"
|
|
18427
|
+
};
|
|
18428
|
+
}
|
|
18429
|
+
function getRoutingStatus(state) {
|
|
18430
|
+
return `Routing: ${state.activated ? "active" : "inactive"} (${state.routingMode})`;
|
|
18431
|
+
}
|
|
18432
|
+
function activationWarning(outcome, nextAction) {
|
|
18433
|
+
switch (outcome) {
|
|
18434
|
+
case "blocked":
|
|
18435
|
+
return `Activation warning: ${nextAction} Activation cannot claim safe readiness.`;
|
|
18436
|
+
case "requires_approval":
|
|
18437
|
+
return `Review needed: ${nextAction}`;
|
|
18438
|
+
case "warn":
|
|
18439
|
+
return `Note: ${nextAction}`;
|
|
18440
|
+
case "pass":
|
|
18441
|
+
return null;
|
|
18442
|
+
}
|
|
18443
|
+
}
|
|
18444
|
+
function handleActivate(dir, options) {
|
|
18445
|
+
const state = checkActivation(dir);
|
|
18446
|
+
const guardResult = scan(dir);
|
|
18447
|
+
const receipt = createReceipt(guardResult);
|
|
18448
|
+
const receiptPath = storeReceipt(dir, receipt);
|
|
18449
|
+
const guardLine = renderSummary(guardResult);
|
|
18450
|
+
const warning = activationWarning(guardResult.policyOutcome, guardResult.nextBestAction);
|
|
18451
|
+
const lines = [getRoutingStatus(state), guardLine];
|
|
18452
|
+
if (warning) lines.push(warning);
|
|
18453
|
+
lines.push(`Receipt: ${receiptPath}`);
|
|
18454
|
+
if (options.json) {
|
|
18455
|
+
return {
|
|
18456
|
+
exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
|
|
18457
|
+
stdout: lines.join("\n"),
|
|
18458
|
+
json: {
|
|
18459
|
+
activated: state.activated,
|
|
18460
|
+
artifactGuard: {
|
|
18461
|
+
policyOutcome: guardResult.policyOutcome,
|
|
18462
|
+
riskScore: guardResult.riskScore,
|
|
18463
|
+
findingsCount: guardResult.findings.length,
|
|
18464
|
+
counts: guardResult.counts,
|
|
18465
|
+
nextBestAction: guardResult.nextBestAction
|
|
18466
|
+
},
|
|
18467
|
+
receiptPath
|
|
18468
|
+
}
|
|
18469
|
+
};
|
|
18470
|
+
}
|
|
18471
|
+
return {
|
|
18472
|
+
exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
|
|
18473
|
+
stdout: lines.join("\n")
|
|
18474
|
+
};
|
|
18475
|
+
}
|
|
18476
|
+
function handleStatus(dir, options) {
|
|
18477
|
+
const state = checkActivation(dir);
|
|
18478
|
+
const status = getRoutingStatus(state);
|
|
18479
|
+
const guardResult = scan(dir);
|
|
18480
|
+
if (options.json) {
|
|
18481
|
+
return {
|
|
18482
|
+
exitCode: 0,
|
|
18483
|
+
stdout: `${status}
|
|
18484
|
+
${renderSummary(guardResult)}`,
|
|
18485
|
+
json: {
|
|
18486
|
+
routing: {
|
|
18487
|
+
active: state.activated,
|
|
18488
|
+
mode: state.routingMode,
|
|
18489
|
+
localFirst: state.localFirst
|
|
18490
|
+
},
|
|
18491
|
+
artifactGuard: {
|
|
18492
|
+
policyOutcome: guardResult.policyOutcome,
|
|
18493
|
+
riskScore: guardResult.riskScore,
|
|
18494
|
+
findingsCount: guardResult.findings.length
|
|
18495
|
+
}
|
|
18496
|
+
}
|
|
18497
|
+
};
|
|
18498
|
+
}
|
|
18499
|
+
return { exitCode: 0, stdout: `${status}
|
|
18500
|
+
${renderSummary(guardResult)}` };
|
|
18501
|
+
}
|
|
18502
|
+
function handleDoctor(dir, options) {
|
|
18503
|
+
const state = checkActivation(dir);
|
|
18504
|
+
const guardResult = scan(dir);
|
|
18505
|
+
const lines = [
|
|
18506
|
+
getRoutingStatus(state),
|
|
18507
|
+
`Registry: ${state.registryLoaded ? "loaded" : "not loaded"}`,
|
|
18508
|
+
`Cloud: ${state.cloudRoutingEnabled ? "enabled" : "disabled"}`,
|
|
18509
|
+
`Policy: ${state.defaultPolicy}`,
|
|
18510
|
+
renderSummary(guardResult)
|
|
18511
|
+
];
|
|
18512
|
+
if (options.json) {
|
|
18513
|
+
return {
|
|
18514
|
+
exitCode: 0,
|
|
18515
|
+
stdout: lines.join("\n"),
|
|
18516
|
+
json: {
|
|
18517
|
+
...state,
|
|
18518
|
+
artifactGuard: {
|
|
18519
|
+
policyOutcome: guardResult.policyOutcome,
|
|
18520
|
+
riskScore: guardResult.riskScore,
|
|
18521
|
+
findingsCount: guardResult.findings.length,
|
|
18522
|
+
counts: guardResult.counts
|
|
18523
|
+
}
|
|
18524
|
+
}
|
|
18525
|
+
};
|
|
18526
|
+
}
|
|
18527
|
+
return { exitCode: 0, stdout: lines.join("\n") };
|
|
18528
|
+
}
|
|
18529
|
+
function handleReadiness(dir, options) {
|
|
18530
|
+
const state = checkActivation(dir);
|
|
18531
|
+
const guardResult = scan(dir);
|
|
18532
|
+
const receipt = createReceipt(guardResult);
|
|
18533
|
+
storeReceipt(dir, receipt);
|
|
18534
|
+
const latestReceipt = loadLatestReceipt(dir);
|
|
18535
|
+
const artifactKinds = [...new Set(guardResult.artifacts.map((a) => a.kind))];
|
|
18536
|
+
const highestSeverity = guardResult.counts.critical > 0 ? "critical" : guardResult.counts.high > 0 ? "high" : guardResult.counts.medium > 0 ? "medium" : guardResult.counts.low > 0 ? "low" : "none";
|
|
18537
|
+
const blockingCount = guardResult.counts.critical + guardResult.counts.high;
|
|
18538
|
+
const lines = [
|
|
18539
|
+
`Readiness Check`,
|
|
18540
|
+
``,
|
|
18541
|
+
`Routing: ${state.activated ? "active" : "inactive"}`,
|
|
18542
|
+
`Artifact Guard: available`,
|
|
18543
|
+
` Files scanned: ${guardResult.filesScanned}`,
|
|
18544
|
+
` Artifact kinds: ${artifactKinds.length > 0 ? artifactKinds.join(", ") : "none detected"}`,
|
|
18545
|
+
` Highest severity: ${highestSeverity}`,
|
|
18546
|
+
` Policy outcome: ${guardResult.policyOutcome.toUpperCase()}`,
|
|
18547
|
+
` Blocking findings: ${blockingCount}`,
|
|
18548
|
+
` Latest receipt: ${latestReceipt ? "available" : "none"}`,
|
|
18549
|
+
` Next: ${guardResult.nextBestAction}`
|
|
18550
|
+
];
|
|
18551
|
+
if (guardResult.policyOutcome === "blocked") {
|
|
18552
|
+
lines.push(``);
|
|
18553
|
+
lines.push(`Readiness: NOT READY \u2014 critical findings must be resolved`);
|
|
18554
|
+
} else if (guardResult.policyOutcome === "requires_approval") {
|
|
18555
|
+
lines.push(``);
|
|
18556
|
+
lines.push(`Readiness: REVIEW NEEDED \u2014 high-severity findings require approval`);
|
|
18557
|
+
} else if (guardResult.policyOutcome === "warn") {
|
|
18558
|
+
lines.push(``);
|
|
18559
|
+
lines.push(`Readiness: READY (with warnings)`);
|
|
18560
|
+
} else {
|
|
18561
|
+
lines.push(``);
|
|
18562
|
+
lines.push(`Readiness: READY`);
|
|
18563
|
+
}
|
|
18564
|
+
if (options.json) {
|
|
18565
|
+
return {
|
|
18566
|
+
exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
|
|
18567
|
+
stdout: lines.join("\n"),
|
|
18568
|
+
json: {
|
|
18569
|
+
routing: { active: state.activated },
|
|
18570
|
+
artifactGuard: {
|
|
18571
|
+
available: true,
|
|
18572
|
+
filesScanned: guardResult.filesScanned,
|
|
18573
|
+
artifactKinds,
|
|
18574
|
+
highestSeverity,
|
|
18575
|
+
policyOutcome: guardResult.policyOutcome,
|
|
18576
|
+
riskScore: guardResult.riskScore,
|
|
18577
|
+
blockingFindings: blockingCount,
|
|
18578
|
+
counts: guardResult.counts,
|
|
18579
|
+
latestReceipt: latestReceipt ? true : false,
|
|
18580
|
+
nextBestAction: guardResult.nextBestAction
|
|
18581
|
+
},
|
|
18582
|
+
readiness: guardResult.policyOutcome === "blocked" ? "not_ready" : guardResult.policyOutcome === "requires_approval" ? "review_needed" : "ready"
|
|
18583
|
+
}
|
|
18584
|
+
};
|
|
18585
|
+
}
|
|
18586
|
+
return {
|
|
18587
|
+
exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
|
|
18588
|
+
stdout: lines.join("\n")
|
|
18589
|
+
};
|
|
18590
|
+
}
|
|
18591
|
+
function handleReceipt(dir, options) {
|
|
18592
|
+
const sub = options.receiptSubcommand ?? "latest";
|
|
18593
|
+
if (sub === "latest") {
|
|
18594
|
+
const receipt = loadLatestReceipt(dir);
|
|
18595
|
+
if (!receipt) {
|
|
18596
|
+
return { exitCode: 0, stdout: "No artifact guard receipt found. Run `avorelo guard scan` first." };
|
|
18597
|
+
}
|
|
18598
|
+
if (options.json) {
|
|
18599
|
+
return {
|
|
18600
|
+
exitCode: 0,
|
|
18601
|
+
stdout: JSON.stringify(receipt, null, 2),
|
|
18602
|
+
json: receipt
|
|
18603
|
+
};
|
|
18604
|
+
}
|
|
18605
|
+
const lines = [
|
|
18606
|
+
`Latest Artifact Guard Receipt`,
|
|
18607
|
+
` Type: ${receipt.receiptType}`,
|
|
18608
|
+
` Scanned: ${receipt.scannedAt}`,
|
|
18609
|
+
` Files: ${receipt.filesScanned}`,
|
|
18610
|
+
` Findings: ${receipt.findingSummary.total}`,
|
|
18611
|
+
` Risk score: ${receipt.riskScore}/10`,
|
|
18612
|
+
` Outcome: ${receipt.policyOutcome.toUpperCase()}`,
|
|
18613
|
+
` Next: ${receipt.nextBestAction}`,
|
|
18614
|
+
` Raw content stored: ${receipt.contentStored}`
|
|
18615
|
+
];
|
|
18616
|
+
return { exitCode: 0, stdout: lines.join("\n") };
|
|
18617
|
+
}
|
|
18618
|
+
return { exitCode: 2, stdout: "Usage: avorelo receipt latest" };
|
|
18619
|
+
}
|
|
18620
|
+
function handleGuard(dir, options) {
|
|
18621
|
+
const sub = options.guardSubcommand ?? "scan";
|
|
18622
|
+
if (sub === "explain") {
|
|
18623
|
+
const ruleId = options.guardArgs?.[0];
|
|
18624
|
+
if (!ruleId) {
|
|
18625
|
+
return { exitCode: 2, stdout: "Usage: avorelo guard explain <ruleId>" };
|
|
18626
|
+
}
|
|
18627
|
+
const rule = getRule(ruleId);
|
|
18628
|
+
if (!rule) {
|
|
18629
|
+
return { exitCode: 2, stdout: `Unknown rule: ${ruleId}` };
|
|
18630
|
+
}
|
|
18631
|
+
const lines = [
|
|
18632
|
+
`Rule: ${rule.id}`,
|
|
18633
|
+
`Title: ${rule.title}`,
|
|
18634
|
+
`Severity: ${rule.severity}`,
|
|
18635
|
+
`Category: ${rule.category}`,
|
|
18636
|
+
`Description: ${rule.description}`,
|
|
18637
|
+
`Recommended action: ${rule.recommendedAction}`,
|
|
18638
|
+
`Applies to: ${rule.artifactKinds === "all" ? "all artifact kinds" : rule.artifactKinds.join(", ")}`
|
|
18639
|
+
];
|
|
18640
|
+
return { exitCode: 0, stdout: lines.join("\n") };
|
|
18641
|
+
}
|
|
18642
|
+
const result3 = scan(dir);
|
|
18643
|
+
const receipt = createReceipt(result3);
|
|
18644
|
+
const receiptPath = storeReceipt(dir, receipt);
|
|
18645
|
+
if (options.json) {
|
|
18646
|
+
return {
|
|
18647
|
+
exitCode: result3.policyOutcome === "blocked" ? 1 : 0,
|
|
18648
|
+
stdout: toJson(result3),
|
|
18649
|
+
json: result3
|
|
18650
|
+
};
|
|
18651
|
+
}
|
|
18652
|
+
if (options.ci) {
|
|
18653
|
+
return {
|
|
18654
|
+
exitCode: result3.policyOutcome === "blocked" ? 1 : 0,
|
|
18655
|
+
stdout: toJson(result3)
|
|
18656
|
+
};
|
|
18657
|
+
}
|
|
18658
|
+
const report = renderReport(result3);
|
|
18659
|
+
return {
|
|
18660
|
+
exitCode: result3.policyOutcome === "blocked" ? 1 : 0,
|
|
18661
|
+
stdout: `${report}
|
|
18662
|
+
Receipt: ${receiptPath}`
|
|
18663
|
+
};
|
|
18664
|
+
}
|
|
18665
|
+
function handleCli(options, dir) {
|
|
18666
|
+
switch (options.command) {
|
|
18667
|
+
case "activate":
|
|
18668
|
+
return handleActivate(dir, options);
|
|
18669
|
+
case "status":
|
|
18670
|
+
return handleStatus(dir, options);
|
|
18671
|
+
case "doctor":
|
|
18672
|
+
return handleDoctor(dir, options);
|
|
18673
|
+
case "guard":
|
|
18674
|
+
return handleGuard(dir, options);
|
|
18675
|
+
case "readiness":
|
|
18676
|
+
return handleReadiness(dir, options);
|
|
18677
|
+
case "receipt":
|
|
18678
|
+
return handleReceipt(dir, options);
|
|
18679
|
+
case "run":
|
|
18680
|
+
return { exitCode: 0, stdout: "run not implemented in guard handler" };
|
|
18681
|
+
}
|
|
18682
|
+
}
|
|
18683
|
+
|
|
17611
18684
|
// src/avorelo/surfaces/cli/avorelo.ts
|
|
17612
18685
|
function arg(args, name, dflt) {
|
|
17613
18686
|
const i = args.indexOf(name);
|
|
@@ -17695,6 +18768,10 @@ function help() {
|
|
|
17695
18768
|
"",
|
|
17696
18769
|
" context check [--target <dir>] [--json] [--strict] [--ci] Agent context integrity check",
|
|
17697
18770
|
"",
|
|
18771
|
+
" guard scan [--target <dir>] [--json] [--ci] Scan agent artifacts for risks",
|
|
18772
|
+
" guard explain <rule-id> Explain a detection rule",
|
|
18773
|
+
" receipt latest [--target <dir>] [--json] Show latest artifact guard receipt",
|
|
18774
|
+
"",
|
|
17698
18775
|
" dogfood-learning status [--target <dir>] [--json] Show learning status",
|
|
17699
18776
|
" dogfood-learning preview [--target <dir>] [--json] Preview sanitized learning payload",
|
|
17700
18777
|
" dogfood-learning send [--target <dir>] [--dry-run] [--json] Send or queue learning signal",
|
|
@@ -17738,8 +18815,8 @@ async function cmdActivate(args) {
|
|
|
17738
18815
|
const r2 = activate(target, { approve: true });
|
|
17739
18816
|
persistReceipt(target, r2.receipt);
|
|
17740
18817
|
const state2 = runFullActivation(target);
|
|
17741
|
-
state2.setupSteps.push({ id: "hooks_installed", label: "Claude Code hooks installed", status: r2.ok ? "passed" : "blocked", evidencePath:
|
|
17742
|
-
if (r2.ok) state2.receipts.push({ id: r2.receipt.receiptId, path:
|
|
18818
|
+
state2.setupSteps.push({ id: "hooks_installed", label: "Claude Code hooks installed", status: r2.ok ? "passed" : "blocked", evidencePath: join66(target, ".claude", "settings.json") });
|
|
18819
|
+
if (r2.ok) state2.receipts.push({ id: r2.receipt.receiptId, path: join66(target, ".avorelo", "receipts", `${r2.receipt.receiptId}.json`), type: "activation_with_hooks" });
|
|
17743
18820
|
persistActivationV2(target, state2);
|
|
17744
18821
|
process.stdout.write(JSON.stringify({ ok: r2.ok, mode: "activate-with-hooks", target, installed: r2.validate.wellFormed, receipt: r2.receipt.receiptId }, null, 2) + "\n");
|
|
17745
18822
|
return r2.ok ? 0 : 1;
|
|
@@ -17762,7 +18839,7 @@ Activation taxonomy: ${preflight.taxonomy}
|
|
|
17762
18839
|
process.stderr.write("Continuing with activation despite warnings...\n\n");
|
|
17763
18840
|
}
|
|
17764
18841
|
const state = runFullActivation(target);
|
|
17765
|
-
const contract = createWorkContract({ contractId: "canonical-activate", objective: "full local-first activation", allowedPaths: [
|
|
18842
|
+
const contract = createWorkContract({ contractId: "canonical-activate", objective: "full local-first activation", allowedPaths: [join66(target, ".avorelo")], planTier: "Free" });
|
|
17766
18843
|
const ledger = new StateLedger();
|
|
17767
18844
|
const receipt = writeReceipt(ledger, {
|
|
17768
18845
|
contractId: contract.contractId,
|
|
@@ -17777,7 +18854,7 @@ Activation taxonomy: ${preflight.taxonomy}
|
|
|
17777
18854
|
redactionClasses: [],
|
|
17778
18855
|
receiptId: "rcpt_canonical_activation"
|
|
17779
18856
|
});
|
|
17780
|
-
state.receipts.push({ id: receipt.receiptId, path:
|
|
18857
|
+
state.receipts.push({ id: receipt.receiptId, path: join66(target, ".avorelo", "receipts", `${receipt.receiptId}.json`), type: "canonical_activation" });
|
|
17781
18858
|
persistActivationV2(target, state);
|
|
17782
18859
|
persistReceipt(target, receipt);
|
|
17783
18860
|
const detection = runFullDetection(target);
|
|
@@ -17790,7 +18867,7 @@ Activation taxonomy: ${preflight.taxonomy}
|
|
|
17790
18867
|
` Mode: ${state.activationMode}`,
|
|
17791
18868
|
` Scope: ${scope}`,
|
|
17792
18869
|
` Status: ${state.activationStatus}`,
|
|
17793
|
-
` State: ${
|
|
18870
|
+
` State: ${join66(target, ACTIVATION_STATE_DIR, ACTIVATION_STATE_FILE)}`
|
|
17794
18871
|
];
|
|
17795
18872
|
if (fv.found.length > 0) {
|
|
17796
18873
|
lines.push("", " Found:");
|
|
@@ -18071,7 +19148,7 @@ function cmdStatus(args) {
|
|
|
18071
19148
|
` activation: ${state.activationStatus}`,
|
|
18072
19149
|
` mode: ${state.activationMode}`,
|
|
18073
19150
|
` contract: ${state.contract}`,
|
|
18074
|
-
` state: ${
|
|
19151
|
+
` state: ${join66(target, ACTIVATION_STATE_DIR, ACTIVATION_STATE_FILE)}`,
|
|
18075
19152
|
` hooks: ${v.installed ? v.wellFormed ? "installed (6/6)" : `partial` : "not installed"}`
|
|
18076
19153
|
];
|
|
18077
19154
|
if (isV2) {
|
|
@@ -18144,7 +19221,7 @@ function cmdVerify(args) {
|
|
|
18144
19221
|
}
|
|
18145
19222
|
const input = loadProofInput(target);
|
|
18146
19223
|
if (input) {
|
|
18147
|
-
const contract = createWorkContract({ contractId: "verify", objective: input.objective ?? "verify real-workflow proof", allowedPaths: [
|
|
19224
|
+
const contract = createWorkContract({ contractId: "verify", objective: input.objective ?? "verify real-workflow proof", allowedPaths: [join66(target, "src")], planTier: "Free" });
|
|
18148
19225
|
const r2 = evaluateProof({ contract, artifacts: input.artifacts, readbacks: input.readbacks, dir: target, sampleSize: input.sampleSize, receiptId: "rcpt_verify" });
|
|
18149
19226
|
process.stdout.write(JSON.stringify({ scope: "full", activationState: { valid: true }, proof: { decision: r2.decision, confidence: r2.confidence } }, null, 2) + "\n");
|
|
18150
19227
|
return r2.decision === "STOP_DONE" ? 0 : 1;
|
|
@@ -18153,13 +19230,13 @@ function cmdVerify(args) {
|
|
|
18153
19230
|
return 0;
|
|
18154
19231
|
}
|
|
18155
19232
|
function cmdSite(args) {
|
|
18156
|
-
const outDir = arg(args, "--out",
|
|
19233
|
+
const outDir = arg(args, "--out", join66(process.cwd(), ".avorelo", "site"));
|
|
18157
19234
|
const r2 = buildSite(outDir);
|
|
18158
19235
|
process.stdout.write(JSON.stringify({ ok: r2.ok, outDir: r2.outDir, indexPath: r2.indexPath, pages: r2.pages }, null, 2) + "\n");
|
|
18159
19236
|
return r2.ok ? 0 : 1;
|
|
18160
19237
|
}
|
|
18161
19238
|
function cmdServe(args) {
|
|
18162
|
-
const outDir = arg(args, "--out",
|
|
19239
|
+
const outDir = arg(args, "--out", join66(process.cwd(), ".avorelo", "site"));
|
|
18163
19240
|
const port = Number(arg(args, "--port", "0"));
|
|
18164
19241
|
buildSite(outDir);
|
|
18165
19242
|
serve(outDir, { port: Number.isFinite(port) ? port : 0 }).then((h) => {
|
|
@@ -18191,7 +19268,7 @@ function cmdServe(args) {
|
|
|
18191
19268
|
}
|
|
18192
19269
|
function readStdinJson() {
|
|
18193
19270
|
try {
|
|
18194
|
-
const raw =
|
|
19271
|
+
const raw = readFileSync50(0, "utf8");
|
|
18195
19272
|
return raw.trim() ? JSON.parse(raw) : {};
|
|
18196
19273
|
} catch {
|
|
18197
19274
|
return {};
|
|
@@ -18215,9 +19292,9 @@ function cmdLifecycleHook(args) {
|
|
|
18215
19292
|
const sourceLabel = String(ccEvent.tool_name ?? "").toLowerCase().includes("mcp") ? "tool_output" : "tool_output";
|
|
18216
19293
|
const r3 = scanContent({ content: toolOutput, sourceKind: sourceLabel });
|
|
18217
19294
|
try {
|
|
18218
|
-
const dir =
|
|
18219
|
-
|
|
18220
|
-
appendFileSync11(
|
|
19295
|
+
const dir = join66(cwd, ".avorelo", "secret-boundary");
|
|
19296
|
+
mkdirSync40(dir, { recursive: true });
|
|
19297
|
+
appendFileSync11(join66(dir, "receipts.jsonl"), JSON.stringify(redact(r3.receipt).value) + "\n");
|
|
18221
19298
|
} catch {
|
|
18222
19299
|
}
|
|
18223
19300
|
process.stdout.write(JSON.stringify({
|
|
@@ -18234,13 +19311,13 @@ function cmdLifecycleHook(args) {
|
|
|
18234
19311
|
}
|
|
18235
19312
|
const looksLikeToolRequest = payloadRaw && ccEvent.tool !== void 0 && ccEvent.workingDir !== void 0;
|
|
18236
19313
|
const req = looksLikeToolRequest ? ccEvent : mapClaudeCodeEvent(ccEvent, cwd);
|
|
18237
|
-
const contract = createWorkContract({ contractId: "hook", objective: "lifecycle hook", allowedPaths: [
|
|
19314
|
+
const contract = createWorkContract({ contractId: "hook", objective: "lifecycle hook", allowedPaths: [join66(cwd, "src")], planTier: "Free" });
|
|
18238
19315
|
const r2 = handleLifecycleHook(event, req, { contract });
|
|
18239
19316
|
try {
|
|
18240
|
-
const dir =
|
|
18241
|
-
|
|
19317
|
+
const dir = join66(cwd, ".avorelo", "events");
|
|
19318
|
+
mkdirSync40(dir, { recursive: true });
|
|
18242
19319
|
const entry = redact({ ts: Date.now(), event: r2.event, tool: req.tool, verdict: r2.verdict, reasonCodes: r2.reasonCodes, redactionClasses: r2.redactionClasses, latencyMs: r2.latencyMs }).value;
|
|
18243
|
-
appendFileSync11(
|
|
19320
|
+
appendFileSync11(join66(dir, "hook-fires.jsonl"), JSON.stringify(entry) + "\n");
|
|
18244
19321
|
} catch {
|
|
18245
19322
|
}
|
|
18246
19323
|
if (r2.exitCode === 2) {
|
|
@@ -18647,13 +19724,13 @@ function cmdTokenCost(args) {
|
|
|
18647
19724
|
}
|
|
18648
19725
|
if (sub === "import" || sub === "validate") {
|
|
18649
19726
|
const file = arg(args, "--file");
|
|
18650
|
-
if (!file || !
|
|
19727
|
+
if (!file || !existsSync64(file)) {
|
|
18651
19728
|
process.stderr.write("Usage: avorelo token-cost " + sub + " --file <path> [--json]\n");
|
|
18652
19729
|
return 2;
|
|
18653
19730
|
}
|
|
18654
19731
|
let parsed;
|
|
18655
19732
|
try {
|
|
18656
|
-
parsed = JSON.parse(
|
|
19733
|
+
parsed = JSON.parse(readFileSync50(file, "utf8"));
|
|
18657
19734
|
} catch {
|
|
18658
19735
|
process.stderr.write("Invalid JSON file.\n");
|
|
18659
19736
|
return 1;
|
|
@@ -18794,9 +19871,9 @@ function cmdContextCheck(args) {
|
|
|
18794
19871
|
const ci = args.includes("--ci");
|
|
18795
19872
|
const wcPath = arg(args, "--work-contract");
|
|
18796
19873
|
let workContract;
|
|
18797
|
-
if (wcPath &&
|
|
19874
|
+
if (wcPath && existsSync64(wcPath)) {
|
|
18798
19875
|
try {
|
|
18799
|
-
workContract = JSON.parse(
|
|
19876
|
+
workContract = JSON.parse(readFileSync50(wcPath, "utf8"));
|
|
18800
19877
|
} catch {
|
|
18801
19878
|
}
|
|
18802
19879
|
}
|
|
@@ -18854,12 +19931,52 @@ function cmdContext(args) {
|
|
|
18854
19931
|
].filter(Boolean).join("\n"));
|
|
18855
19932
|
return 0;
|
|
18856
19933
|
}
|
|
19934
|
+
function cmdGuard(args) {
|
|
19935
|
+
const sub = args[0];
|
|
19936
|
+
const target = arg(args, "--target", process.cwd());
|
|
19937
|
+
const asJson = args.includes("--json");
|
|
19938
|
+
const ci = args.includes("--ci");
|
|
19939
|
+
if (sub === "scan") {
|
|
19940
|
+
const output = handleCli(
|
|
19941
|
+
{ command: "guard", json: asJson, verbose: false, guardSubcommand: "scan", ci },
|
|
19942
|
+
target
|
|
19943
|
+
);
|
|
19944
|
+
process.stdout.write(output.stdout + "\n");
|
|
19945
|
+
return output.exitCode;
|
|
19946
|
+
}
|
|
19947
|
+
if (sub === "explain") {
|
|
19948
|
+
const ruleId = args[1];
|
|
19949
|
+
const output = handleCli(
|
|
19950
|
+
{ command: "guard", json: false, verbose: false, guardSubcommand: "explain", guardArgs: [ruleId] },
|
|
19951
|
+
target
|
|
19952
|
+
);
|
|
19953
|
+
process.stdout.write(output.stdout + "\n");
|
|
19954
|
+
return output.exitCode;
|
|
19955
|
+
}
|
|
19956
|
+
process.stderr.write("Usage: avorelo guard <scan|explain> [args]\n");
|
|
19957
|
+
return 2;
|
|
19958
|
+
}
|
|
19959
|
+
function cmdReceipt(args) {
|
|
19960
|
+
const sub = args[0];
|
|
19961
|
+
const target = arg(args, "--target", process.cwd());
|
|
19962
|
+
const asJson = args.includes("--json");
|
|
19963
|
+
if (sub === "latest" || sub === void 0) {
|
|
19964
|
+
const output = handleCli(
|
|
19965
|
+
{ command: "receipt", json: asJson, verbose: false, receiptSubcommand: "latest" },
|
|
19966
|
+
target
|
|
19967
|
+
);
|
|
19968
|
+
process.stdout.write(output.stdout + "\n");
|
|
19969
|
+
return output.exitCode;
|
|
19970
|
+
}
|
|
19971
|
+
process.stderr.write("Usage: avorelo receipt latest [--target <dir>] [--json]\n");
|
|
19972
|
+
return 2;
|
|
19973
|
+
}
|
|
18857
19974
|
function cmdSecretBoundary(args) {
|
|
18858
19975
|
const sub = args[0];
|
|
18859
19976
|
const asJson = args.includes("--json");
|
|
18860
19977
|
let content = arg(args, "--content");
|
|
18861
19978
|
const file = arg(args, "--file");
|
|
18862
|
-
if (file &&
|
|
19979
|
+
if (file && existsSync64(file)) content = readFileSync50(file, "utf8");
|
|
18863
19980
|
if (content === void 0) content = "";
|
|
18864
19981
|
if (sub === "scan" || sub === void 0) {
|
|
18865
19982
|
const r2 = scanContent({ content, sourceKind: file ? "file" : "tool_output" });
|
|
@@ -18926,7 +20043,7 @@ function cmdExplain(args) {
|
|
|
18926
20043
|
const changedFiles = [];
|
|
18927
20044
|
for (const { adapter } of detected) {
|
|
18928
20045
|
const surface = adapter.getInstructionSurface(target);
|
|
18929
|
-
if (surface &&
|
|
20046
|
+
if (surface && existsSync64(surface)) changedFiles.push(surface);
|
|
18930
20047
|
}
|
|
18931
20048
|
if (changedFiles.length > 0) {
|
|
18932
20049
|
lines.push(" Changed:");
|
|
@@ -18940,7 +20057,7 @@ function cmdExplain(args) {
|
|
|
18940
20057
|
lines.push("");
|
|
18941
20058
|
lines.push(` Hooks: ${hookValidation.installed ? "installed" : "not installed"}`);
|
|
18942
20059
|
lines.push(" Cloud: off (local-first)");
|
|
18943
|
-
lines.push(` Receipts: ${
|
|
20060
|
+
lines.push(` Receipts: ${join66(target, ".avorelo", "receipts")}`);
|
|
18944
20061
|
const sessionStatus = getSessionStatus(target);
|
|
18945
20062
|
if (sessionStatus) {
|
|
18946
20063
|
lines.push("");
|
|
@@ -19100,7 +20217,7 @@ function cmdFeedback(args) {
|
|
|
19100
20217
|
}
|
|
19101
20218
|
if (sub === "share") {
|
|
19102
20219
|
const file = arg(args, "--file");
|
|
19103
|
-
if (!file || !
|
|
20220
|
+
if (!file || !existsSync64(file)) {
|
|
19104
20221
|
process.stderr.write("Usage: avorelo feedback share --file <bundle-path>\n");
|
|
19105
20222
|
return 2;
|
|
19106
20223
|
}
|
|
@@ -19404,7 +20521,7 @@ No loop metadata found for ${loopId}.
|
|
|
19404
20521
|
if (sub === "doctor") {
|
|
19405
20522
|
const issues = [];
|
|
19406
20523
|
const ok = [];
|
|
19407
|
-
if (
|
|
20524
|
+
if (existsSync64(join66(target, ".git"))) {
|
|
19408
20525
|
ok.push("Git repository detected");
|
|
19409
20526
|
} else {
|
|
19410
20527
|
issues.push("Not a git repository \u2014 loop needs git for drift detection");
|
|
@@ -19414,12 +20531,12 @@ No loop metadata found for ${loopId}.
|
|
|
19414
20531
|
} else {
|
|
19415
20532
|
issues.push("Claude Code CLI not found \u2014 install it or ensure `claude` is on PATH");
|
|
19416
20533
|
}
|
|
19417
|
-
const testDir =
|
|
20534
|
+
const testDir = join66(target, ".avorelo", "loops");
|
|
19418
20535
|
try {
|
|
19419
|
-
|
|
19420
|
-
const testFile =
|
|
19421
|
-
|
|
19422
|
-
|
|
20536
|
+
mkdirSync40(testDir, { recursive: true });
|
|
20537
|
+
const testFile = join66(testDir, ".doctor_probe");
|
|
20538
|
+
writeFileSync38(testFile, "probe");
|
|
20539
|
+
unlinkSync5(testFile);
|
|
19423
20540
|
ok.push("Storage writable (.avorelo/loops/)");
|
|
19424
20541
|
} catch {
|
|
19425
20542
|
issues.push("Cannot write to .avorelo/loops/ \u2014 check permissions");
|
|
@@ -19460,11 +20577,11 @@ function cmdUninstallAll(args) {
|
|
|
19460
20577
|
const target = arg(args, "--target", process.cwd());
|
|
19461
20578
|
const adapterResult = uninstallAll(target);
|
|
19462
20579
|
const hookResult = uninstall(target);
|
|
19463
|
-
const removed = [...adapterResult.removed, ...hookResult.restored ? [
|
|
20580
|
+
const removed = [...adapterResult.removed, ...hookResult.restored ? [join66(target, ".claude", "settings.json")] : []];
|
|
19464
20581
|
const preserved = adapterResult.preserved;
|
|
19465
|
-
const avoreloDir2 =
|
|
20582
|
+
const avoreloDir2 = join66(target, ".avorelo");
|
|
19466
20583
|
try {
|
|
19467
|
-
if (
|
|
20584
|
+
if (existsSync64(avoreloDir2)) {
|
|
19468
20585
|
rmSync3(avoreloDir2, { recursive: true, force: true });
|
|
19469
20586
|
removed.push(avoreloDir2);
|
|
19470
20587
|
}
|
|
@@ -20057,7 +21174,7 @@ function cmdConfig(args) {
|
|
|
20057
21174
|
function avoreloVersion() {
|
|
20058
21175
|
for (const rel of ["../package.json", "../../package.json", "../../../package.json", "../../../../package.json"]) {
|
|
20059
21176
|
try {
|
|
20060
|
-
const pkg = JSON.parse(
|
|
21177
|
+
const pkg = JSON.parse(readFileSync50(join66(import.meta.dirname, rel), "utf8"));
|
|
20061
21178
|
if (pkg.name === "avorelo" && pkg.version) return pkg.version;
|
|
20062
21179
|
} catch {
|
|
20063
21180
|
}
|
|
@@ -20168,6 +21285,10 @@ function main(argv) {
|
|
|
20168
21285
|
return cmdReadiness(rest);
|
|
20169
21286
|
case "loop":
|
|
20170
21287
|
return cmdLoop(rest);
|
|
21288
|
+
case "guard":
|
|
21289
|
+
return cmdGuard(rest);
|
|
21290
|
+
case "receipt":
|
|
21291
|
+
return cmdReceipt(rest);
|
|
20171
21292
|
default:
|
|
20172
21293
|
return help();
|
|
20173
21294
|
}
|