@blundergoat/goat-flow
Version:
AI coding agent harness and local dashboard for Claude Code, OpenAI Codex, Google Antigravity, and GitHub Copilot - setup audits, guardrails, structured skills, deny hooks, and persistent learning loops.
172 lines • 9.82 kB
JavaScript
const CAPABILITY_LABELS = {
"shell-dangerous": "Dangerous shell commands",
"shell-pipe-to-shell": "Pipe-to-shell commands",
"secret-file-read": "Secret file-read paths",
"secret-shell-read": "Secret shell-read commands",
"hook-registration": "Pre-tool hook registration",
"hook-self-test": "Deny hook self-test",
"file-read-restrictions": "General file-read restrictions",
"file-write-restrictions": "General file-write restrictions",
"provider-native-enforcement": "Provider-native enforcement",
};
function capability(id, status, sources, summary, evidence) {
return {
id,
label: CAPABILITY_LABELS[id],
status,
sources,
summary,
evidence,
};
}
/** Initialize every status counter so dashboard readers never infer missing keys as zero silently. */
function emptySummary() {
return { hard: 0, limited: 0, soft: 0, missing: 0, unknown: 0 };
}
function summarize(capabilities) {
const summary = emptySummary();
for (const enforcementCapability of capabilities) {
summary[enforcementCapability.status]++;
}
return summary;
}
/** Treat settings and registered hooks as active deny mechanisms; a present script alone is not enough. */
function hasActiveMechanicalDeny(agentFacts) {
if (agentFacts.hooks.denyIsConfigBased)
return true;
if (agentFacts.agent.denyMechanism &&
agentFacts.agent.denyMechanism.type !== "deny-script" &&
agentFacts.settings.hasDenyPatterns) {
return true;
}
return agentFacts.hooks.denyIsRegistered;
}
function shellCapability(agentFacts, id, covered, coveredSummary, missingSummary) {
const denyExists = agentFacts.hooks.denyExists || agentFacts.hooks.denyIsConfigBased;
if (!denyExists) {
return capability(id, "missing", ["not-observed"], missingSummary, []);
}
if (!covered) {
return capability(id, "missing", ["local-hook"], missingSummary, [
"AgentFacts.hooks",
]);
}
if (hasActiveMechanicalDeny(agentFacts)) {
return capability(id, "hard", ["local-hook"], coveredSummary, [
"AgentFacts.hooks",
]);
}
return capability(id, "limited", ["local-hook"], `${coveredSummary}; hook coverage exists but registration was not proved`, ["AgentFacts.hooks.denyIsRegistered"]);
}
/** Report file-tool secret protection separately because Bash commands bypass file-read denies. */
function secretFileReadCapability(agentFacts) {
if (agentFacts.hooks.readDenyCoversSecrets) {
return capability("secret-file-read", "hard", ["local-settings"], "Settings or Codex permission profile deny known secret-bearing file paths", ["AgentFacts.hooks.readDenyCoversSecrets"]);
}
if (agentFacts.agent.denyMechanism?.type === "deny-script") {
return capability("secret-file-read", "limited", ["local-hook"], "Script-only deny can block shell reads, but no file-read deny layer is available", ["AgentProfile.denyMechanism"]);
}
return capability("secret-file-read", "missing", ["not-observed"], "No settings or permission-profile secret file-read deny coverage was observed", ["AgentFacts.hooks.readDenyCoversSecrets"]);
}
/** Report shell secret protection separately because settings-level read denies do not bind Bash. */
function secretShellReadCapability(agentFacts) {
if (!agentFacts.hooks.bashDenyCoversSecrets) {
return capability("secret-shell-read", "missing", ["local-hook"], "Bash deny hook does not prove direct literal secret shell-read blocking", ["AgentFacts.hooks.bashDenyCoversSecrets"]);
}
if (hasActiveMechanicalDeny(agentFacts)) {
return capability("secret-shell-read", "hard", ["local-hook"], "Bash deny hook blocks direct literal secret shell-read commands", ["AgentFacts.hooks.bashDenyCoversSecrets"]);
}
return capability("secret-shell-read", "limited", ["local-hook"], "Bash deny hook covers secret shell reads, but hook registration was not proved", ["AgentFacts.hooks.denyIsRegistered"]);
}
/** Distinguish hook existence from registration so static files are not mistaken for active runtime wiring. */
function hookRegistrationCapability(agentFacts) {
if (!agentFacts.hooks.denyExists && !agentFacts.hooks.denyIsConfigBased) {
return capability("hook-registration", "missing", ["not-observed"], "No deny mechanism was observed", []);
}
if (agentFacts.hooks.denyIsConfigBased && !agentFacts.hooks.denyExists) {
return capability("hook-registration", "soft", ["local-settings"], "Settings-based deny exists without a shell hook registration surface", ["AgentFacts.hooks.denyIsConfigBased"]);
}
const preToolEvent = agentFacts.agent.hookEvents?.preTool ?? "pre-tool";
if (agentFacts.hooks.denyIsRegistered) {
return capability("hook-registration", "hard", ["local-hook"], `Deny hook is registered as ${preToolEvent}`, ["AgentFacts.hooks.denyIsRegistered"]);
}
return capability("hook-registration", "missing", ["local-hook"], `Deny hook exists but is not registered as ${preToolEvent}`, ["AgentFacts.hooks.denyIsRegistered"]);
}
function denyCheck(agentScope) {
return agentScope?.checks.find((check) => check.id === "agent-guardrails");
}
function hookSelfTestCapability(agentFacts, options) {
if (!agentFacts.hooks.denyExists) {
return capability("hook-self-test", "missing", ["not-observed"], "No deny hook exists to self-test", []);
}
const check = denyCheck(options.agentScope);
if (!check || check.status === "skipped") {
return capability("hook-self-test", "limited", ["local-hook"], "Deny hook self-test was not run in this aggregate audit context", ["agent-guardrails"]);
}
if (check.status === "fail") {
return capability("hook-self-test", "missing", ["local-hook"], check.failure?.message ??
"Deny hook self-test or static deny check failed", ["agent-guardrails"]);
}
if (options.denyMechanismEvidenceLevel === "full" ||
options.denyMechanismEvidenceLevel === undefined) {
return capability("hook-self-test", "hard", ["runtime-self-test"], "Deny hook self-test and runtime-shaped payload smoke passed in this audit run", ["agent-guardrails"]);
}
return capability("hook-self-test", "limited", ["local-hook"], `Deny hook static checks passed, but runtime self-test was skipped in ${options.denyMechanismEvidenceLevel} evidence mode`, ["agent-guardrails"]);
}
/** Keep provider-native breadth advisory because manifest capability does not prove runtime enforcement. */
function providerNativeCapability(agentFacts) {
if (agentFacts.agent.denyMechanism === null) {
return capability("provider-native-enforcement", "missing", ["manifest"], "Manifest records no project-local deny mechanism for this agent", ["AgentProfile.denyMechanism"]);
}
const mechanism = agentFacts.agent.denyMechanism.type;
if (mechanism === "deny-script") {
return capability("provider-native-enforcement", "limited", ["manifest"], "Manifest records script-only deny; provider-native breadth was not claimed", ["AgentProfile.denyMechanism"]);
}
if (mechanism === "both") {
return capability("provider-native-enforcement", "limited", ["manifest"], "Manifest records settings plus script deny; provider-native breadth was not verified", ["AgentProfile.denyMechanism"]);
}
return capability("provider-native-enforcement", "soft", ["manifest"], "Manifest records settings-based deny; provider-native breadth was not verified", ["AgentProfile.denyMechanism"]);
}
function broadFilesystemCapability(id) {
return capability(id, "unknown", ["not-observed"], "Not inferred from secret-path coverage, hook installation, or setup pass", []);
}
/**
* Build the advisory enforcement matrix for one agent.
*
* @param agentFacts Extracted local facts for the audited agent.
* @param options Evidence-mode switches from the current audit run.
* @returns Non-gating enforcement capability report for audit and dashboard output.
*/
export function buildAgentEnforcementCapability(agentFacts, options = {}) {
const capabilities = [
shellCapability(agentFacts, "shell-dangerous", agentFacts.hooks.denyBlocksRmRf &&
agentFacts.hooks.denyBlocksGitPush &&
agentFacts.hooks.denyBlocksChmod, "Deny mechanism blocks broad recursive deletion, git push, and chmod 777 patterns", "Deny mechanism does not prove coverage for broad recursive deletion, git push, and chmod 777"),
shellCapability(agentFacts, "shell-pipe-to-shell", agentFacts.hooks.denyBlocksPipeToShell, "Deny mechanism blocks curl|bash and wget|sh style pipe-to-shell patterns", "Deny mechanism does not prove pipe-to-shell blocking"),
secretFileReadCapability(agentFacts),
secretShellReadCapability(agentFacts),
hookRegistrationCapability(agentFacts),
hookSelfTestCapability(agentFacts, options),
broadFilesystemCapability("file-read-restrictions"),
broadFilesystemCapability("file-write-restrictions"),
providerNativeCapability(agentFacts),
];
return {
agent: agentFacts.agent.id,
name: agentFacts.agent.name,
advisory: true,
capabilities,
summary: summarize(capabilities),
};
}
/**
* Build the advisory enforcement matrix for every audited agent.
*
* @param agents Extracted local facts for all agents included in the audit.
* @param options Evidence-mode switches from the current audit run.
* @returns Non-gating enforcement reports in the same order as the input agents.
*/
export function buildEnforcementMatrix(agents, options = {}) {
return agents.map((agent) => buildAgentEnforcementCapability(agent, options));
}
//# sourceMappingURL=enforcement.js.map