@zaip-ai/cli 4.9.0 → 5.1.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.
- package/dist/services/adapter-hook-sources.d.ts +3 -0
- package/dist/services/adapter-hook-sources.d.ts.map +1 -1
- package/dist/services/adapter-hook-sources.js +3 -0
- package/dist/services/adapter-hook-sources.js.map +1 -1
- package/dist/services/adapters.d.ts +1 -1
- package/dist/services/adapters.d.ts.map +1 -1
- package/dist/services/adapters.js +5 -2
- package/dist/services/adapters.js.map +1 -1
- package/dist/services/adapters.test.js +1 -1
- package/dist/services/adapters.test.js.map +1 -1
- package/dist/services/codex-conversation-lifecycle-hook.d.ts.map +1 -1
- package/dist/services/codex-conversation-lifecycle-hook.js +16 -6
- package/dist/services/codex-conversation-lifecycle-hook.js.map +1 -1
- package/dist/services/codex-hook-activation.d.ts +1 -1
- package/dist/services/codex-hook-activation.d.ts.map +1 -1
- package/dist/services/codex-hook-activation.js +116 -10
- package/dist/services/codex-hook-activation.js.map +1 -1
- package/dist/services/codex-pre-tool-cli-capture-template.d.ts +2 -0
- package/dist/services/codex-pre-tool-cli-capture-template.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-cli-capture-template.js +104 -0
- package/dist/services/codex-pre-tool-cli-capture-template.js.map +1 -0
- package/dist/services/codex-pre-tool-cloud-template.d.ts +2 -0
- package/dist/services/codex-pre-tool-cloud-template.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-cloud-template.js +190 -0
- package/dist/services/codex-pre-tool-cloud-template.js.map +1 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.d.ts +2 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.js +9 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.js.map +1 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts +5 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.js +19 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.js.map +1 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts +2 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js +107 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js.map +1 -0
- package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.js +96 -210
- package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.test.js +32 -80
- package/dist/services/codex-pre-tool-hook-template.test.js.map +1 -1
- package/dist/services/codex-pre-tool-host-context-template.d.ts +2 -0
- package/dist/services/codex-pre-tool-host-context-template.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-host-context-template.js +139 -0
- package/dist/services/codex-pre-tool-host-context-template.js.map +1 -0
- package/dist/services/codex-pre-tool-support-template.d.ts.map +1 -1
- package/dist/services/codex-pre-tool-support-template.js +61 -101
- package/dist/services/codex-pre-tool-support-template.js.map +1 -1
- package/dist/services/codex-subagent-hook-template.d.ts +1 -1
- package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
- package/dist/services/codex-subagent-hook-template.js +13 -2
- package/dist/services/codex-subagent-hook-template.js.map +1 -1
- package/dist/services/codex-validation-runtime-template.d.ts.map +1 -1
- package/dist/services/codex-validation-runtime-template.js +10 -4
- package/dist/services/codex-validation-runtime-template.js.map +1 -1
- package/dist/services/runtime-template-test-support.d.ts +1 -0
- package/dist/services/runtime-template-test-support.d.ts.map +1 -1
- package/dist/services/runtime-template-test-support.js +23 -2
- package/dist/services/runtime-template-test-support.js.map +1 -1
- package/dist/services/runtime-template.test.js +4 -23
- package/dist/services/runtime-template.test.js.map +1 -1
- package/package.json +1 -1
- package/templates/.agents/README-ENG.md +58 -7
- package/templates/.agents/README.md +46 -10
- package/templates/.agents/config/agents/discovery.json +1 -1
- package/templates/.agents/dashboard/.zaip-managed.json +174 -86
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-BW7Vy1cp.js → CostCalculationGuidePage-uR-asJv_.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-Cp_hEVd8.js → DashboardHelpDialog-CngnpoLL.js} +87 -13
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Prcs-ydy.js → PricingSettingsDialog-Bszw3ekd.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-Cu40wAFF.js → ProjectSettingsPage-IJ1znG4w.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-Bh3YBd3C.js → TraceabilityWorkspace-CsBpHqq_.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-B0Jwz_rk.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-CF7B1Hjn.js → dashboard-localization-D41g5rdo.js} +58 -7
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DOhy6D2-.js → icons-vendor-CjGGux6h.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Bedlm0f7.js +2 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CkPC-yMz.js → react-vendor-B9jZSAKf.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-BEdGoIDm.js → traceability-DFpwb0TN.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
- package/templates/.agents/dashboard/apps/frontend/package.json +2 -2
- package/templates/.agents/dashboard/apps/frontend/src/AppDashboardContent.tsx +3 -0
- package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +16 -7
- package/templates/.agents/dashboard/apps/frontend/src/__tests__/app-composition-support.ts +1 -0
- package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +1 -1
- package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +2 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.navigation.test.tsx +125 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +67 -9
- package/templates/.agents/dashboard/apps/frontend/src/use-dashboard-app-model.tsx +15 -15
- package/templates/.agents/dashboard/apps/frontend/src/use-dashboard-locale.ts +18 -0
- package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +10 -3
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +5 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostGroupsTable.period-values.test.tsx +2 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables-keeps-legacy-codex-identity-fields-blank.test.ts +5 -5
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +36 -43
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-loaders.ts +1 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-formatters.ts +6 -4
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.ts +2 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.ts +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +11 -25
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/cost-summary-defaults.ts +26 -0
- package/templates/.agents/dashboard/modules/operations/package.json +1 -1
- package/templates/.agents/dashboard/modules/operations/src/contracts.ts +11 -16
- package/templates/.agents/dashboard/modules/operations/src/planning-contracts.ts +14 -0
- package/templates/.agents/dashboard/modules/operations/src/react/DecisionTable.tsx +65 -0
- package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +5 -71
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +4 -39
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.navigation.test.tsx +150 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.tsx +7 -3
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +14 -43
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +3 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-contract.ts +51 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +12 -3
- package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +1 -0
- package/templates/.agents/dashboard/modules/operations/src/react/use-operations-default-range.ts +16 -0
- package/templates/.agents/dashboard/modules/operations/src/react/use-operations-filter-sync.ts +66 -0
- package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
- package/templates/.agents/dashboard/packages/branding/package.json +1 -1
- package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
- package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/dashboard-build-fingerprint.mjs +87 -0
- package/templates/.agents/dashboard/packages/local-runtime/dashboard-build-fingerprint.test.mjs +48 -0
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +193 -191
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +169 -169
- package/templates/.agents/dashboard/packages/local-runtime/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/scripts/build-frontend.mjs +29 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +5 -0
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +193 -191
- package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.test.ts +38 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.ts +35 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.test.ts +53 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.ts +18 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/manifest-runs.ts +8 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/server-capture-projection-order.test.ts +118 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.test.ts +90 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.ts +86 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-routes.ts +8 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/server-projection-loaders.ts +13 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-projections.ts +30 -74
- package/templates/.agents/dashboard/packages/local-runtime/src/server-route-handlers.ts +42 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-routes.test.ts +60 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +12 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-command.test.ts +29 -4
- package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-refresh.ts +7 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +51 -54
- package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +28 -45
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-filter-matrix.test.ts +5 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +49 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +44 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-cached-cost-page.ts +11 -10
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-summary.ts +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-work-item-index.test.ts +18 -1
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-build.mjs +12 -5
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-paths.mjs +8 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +1 -0
- package/templates/.agents/dashboard/packages/ui/package.json +1 -1
- package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
- package/templates/.agents/dashboard/tests/e2e/qualify-top-five-trace.mjs +53 -0
- package/templates/.agents/dashboard/tests/e2e/verify-top-five-served.mjs +78 -0
- package/templates/.agents/design/.zaip-managed.json +14 -6
- package/templates/.agents/design/apps/catalog/dist/assets/index-BGxXxXTo.js +72 -0
- package/templates/.agents/design/apps/catalog/dist/index.html +1 -1
- package/templates/.agents/design/apps/catalog/package.json +1 -1
- package/templates/.agents/design/apps/catalog/src/components/TopEntitiesSpecimen.tsx +45 -0
- package/templates/.agents/design/apps/catalog/src/pages/ComponentsPage.tsx +3 -1
- package/templates/.agents/design/apps/catalog/src/stories/Components.stories.tsx +2 -0
- package/templates/.agents/design/packages/local-runtime/package.json +1 -1
- package/templates/.agents/requirements/autonomous-assurance.md +11 -2
- package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
- package/templates/.agents/requirements/comparative-audit.md +1 -1
- package/templates/.agents/requirements/context-engine.md +1 -1
- package/templates/.agents/requirements/cost-observability.md +1 -1
- package/templates/.agents/requirements/dashboard-launch.md +1 -1
- package/templates/.agents/requirements/data-export.md +1 -1
- package/templates/.agents/requirements/design-system.md +1 -1
- package/templates/.agents/requirements/execution-engine.md +1 -1
- package/templates/.agents/requirements/kanban-dashboard.md +1 -1
- package/templates/.agents/requirements/model-orchestration.md +1 -1
- package/templates/.agents/requirements/public-distribution.md +1 -1
- package/templates/.agents/requirements/software-architecture.md +1 -1
- package/templates/.agents/requirements/strategic-improvements.md +1 -1
- package/templates/.agents/requirements/work-lifecycle.md +2 -1
- package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
- package/templates/.agents/rules/run-lifecycle-closure.md +2 -1
- package/templates/.agents/rules/workflow.md +1 -0
- package/templates/.agents/runtime/VERSION +1 -1
- package/templates/.agents/runtime/bin/zaip.mjs +1178 -764
- package/templates/.agents/runtime/checksums.sha256 +6 -6
- package/templates/.agents/runtime/manifest.json +1 -1
- package/templates/.agents/runtime/pnpm-lock.sha256 +1 -1
- package/templates/.agents/runtime/provenance.json +5 -5
- package/templates/.agents/runtime/sbom.cdx.json +59 -59
- package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-execution/references/controlled-release-recovery.md +14 -0
- package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +1 -1
- package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -1
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract-cli.test.mjs +53 -0
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.mjs +12 -0
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.test.mjs +26 -32
- package/templates/.agents/skills/zaip-skill-maintenance-enter/SKILL.md +23 -0
- package/templates/.agents/skills/zaip-skill-maintenance-enter/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.mjs +213 -0
- package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.test.mjs +207 -0
- package/templates/.agents/skills/zaip-skill-maintenance-exit/SKILL.md +21 -0
- package/templates/.agents/skills/zaip-skill-maintenance-exit/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +2 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +37 -8
- package/templates/.agents/zaip-aios/VERSION +1 -1
- package/templates/.agents/zaip-aios/guides/10-seguranca.md +2 -2
- package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +38 -0
- package/templates/template-manifest.json +259 -135
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Co2Jiuzh.js +0 -68
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-qVSJnJ0_.js +0 -2
|
@@ -5,12 +5,15 @@ import { lstat, readFile, realpath } from "node:fs/promises";
|
|
|
5
5
|
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
|
-
import { captureCliTurn, checkDestructivePatterns, configuredDeploymentBranches, gitInvocation, inside, isLiteralReadSequence, requiresShellMutationRun, reviewedContextReadPaths, reviewedReleasePaths, rewriteCliInput, selectHookExecutionWorkspace, shellSyntax, tokenizeLiteralCommand, validateInlineExecution } from "./zaip-pre-tool-support.mjs";
|
|
8
|
+
import { captureCliTurn, checkDestructivePatterns, configuredDeploymentBranches, configuredPermissionProfile, gitInvocation, inside, isLiteralReadSequence, parseHookEvent, readBoundedCanonicalText, requiresShellMutationRun, reviewedContextReadPaths, reviewedReleasePaths, rewriteCliInput, selectHookExecutionWorkspace, selectHookRepositoryRoot, shellSyntax, tokenizeLiteralCommand, validateInlineExecution } from "./zaip-pre-tool-support.mjs";
|
|
9
|
+
import { createCloudValidators } from "./zaip-pre-tool-cloud.mjs";
|
|
9
10
|
const execute = promisify(execFile);
|
|
10
11
|
const nativeAuthority = await import("./zaip-native-tool-authority.mjs").catch(() => null);
|
|
11
12
|
const activation = await import("./zaip-hook-activation.mjs").catch(() => null);
|
|
12
13
|
const installedRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
13
14
|
let repositoryRoot = installedRoot;
|
|
15
|
+
let principalFullAccess = false;
|
|
16
|
+
let principalFullNetwork = false;
|
|
14
17
|
const deletionCommandPattern = /\b(?:Remove-Item|ri|rm|rmdir|rd|del|erase)\b/i;
|
|
15
18
|
const allowedExplicitNetworkHosts = new Set([
|
|
16
19
|
"127.0.0.1", "api.github.com", "github.com", "localhost", "objects.githubusercontent.com", "releases.hashicorp.com", "::1",
|
|
@@ -32,6 +35,10 @@ const homeReferencePattern = /(?:\$HOME\b|\$env:(?:USERPROFILE|HOMEDRIVE|HOMEPAT
|
|
|
32
35
|
const credentialReferencePattern = /(?:^|[\\/\s"'=])(?:\.ssh|\.aws|\.azure|\.kube|\.docker|\.gnupg|\.git-credentials|\.npmrc|\.pypirc|id_(?:rsa|ed25519)|[^\\/\s"']+\.(?:pem|key|p12|pfx))(?:[\\/\s"'=]|$)|(?:^|[\\/\s"'=])\.env(?:\.[A-Za-z0-9_-]+)*(?:[\\/\s"']|$)/i;
|
|
33
36
|
const sensitiveEnvironmentPattern = /(?:\$env:|%|\$)[A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE_KEY|CREDENTIAL)[A-Z0-9_]*%?/i;
|
|
34
37
|
const destructivePatterns = [
|
|
38
|
+
{
|
|
39
|
+
pattern: /\b(?:Restart-Computer|Stop-Computer|Restart-Host|shutdown(?:\.exe)?|reboot|poweroff|halt|Suspend-Computer|hibernate|logoff|Win32Shutdown)\b/i,
|
|
40
|
+
reason: "Local computer restart, shutdown, suspension, hibernation and session termination are blocked.",
|
|
41
|
+
},
|
|
35
42
|
{
|
|
36
43
|
pattern: /\b(?:Clear-Content|shred)\b/i,
|
|
37
44
|
reason: "Unbounded content destruction is blocked.",
|
|
@@ -148,7 +155,7 @@ function repositoryPath(path) {
|
|
|
148
155
|
}
|
|
149
156
|
function isGitPath(path) {
|
|
150
157
|
const scoped = repositoryPath(path).toLowerCase();
|
|
151
|
-
return scoped
|
|
158
|
+
return scoped.split("/").includes(".git");
|
|
152
159
|
}
|
|
153
160
|
|
|
154
161
|
function unquote(value) {
|
|
@@ -172,7 +179,18 @@ function explicitPathCandidates(command) {
|
|
|
172
179
|
}
|
|
173
180
|
return [...candidates].map(unquote).filter(Boolean);
|
|
174
181
|
}
|
|
175
|
-
function
|
|
182
|
+
async function assertCanonicalLiteralPath(path, cwd = repositoryRoot) {
|
|
183
|
+
if (path.split(/[\\/]/).includes("..")) deny("Filesystem traversal is blocked.");
|
|
184
|
+
for (let cursor = resolve(cwd, path);; cursor = dirname(cursor)) {
|
|
185
|
+
try {
|
|
186
|
+
const info = await lstat(cursor);
|
|
187
|
+
if (info.isSymbolicLink() || (await realpath(cursor)).toLowerCase() !== cursor.toLowerCase())
|
|
188
|
+
deny("Filesystem links and aliases are blocked.");
|
|
189
|
+
} catch (error) { if (error?.code !== "ENOENT") deny("Filesystem path could not be safely verified."); }
|
|
190
|
+
if (dirname(cursor) === cursor) break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function validateExplicitFilesystemReferences(command, cwd, reviewedPaths = []) {
|
|
176
194
|
const checked = reviewedPaths.reduce((text, path) => text.replaceAll(path, "REVIEWED_RELEASE_PATH"), command);
|
|
177
195
|
if (homeReferencePattern.test(checked)) {
|
|
178
196
|
deny("Direct user-profile path references are blocked; use repository-scoped paths.");
|
|
@@ -180,7 +198,7 @@ function validateExplicitFilesystemReferences(command, cwd, reviewedPaths = [])
|
|
|
180
198
|
if (credentialReferencePattern.test(checked) || sensitiveEnvironmentPattern.test(checked)) {
|
|
181
199
|
deny("Direct credential and secret access is blocked.");
|
|
182
200
|
}
|
|
183
|
-
if (/\b(?:Get-ChildItem|gci|dir)
|
|
201
|
+
if (/\b(?:Get-ChildItem|gci|dir|Get-Item|gi)(?:\s+-(?:LiteralPath|Path))?\s+(?:Env|Variable|Function|Alias):|\b(?:printenv|set)\s*$/i.test(checked) || /GetEnvironmentVariables\s*\(/i.test(checked)) {
|
|
184
202
|
deny("Bulk environment enumeration is blocked.");
|
|
185
203
|
}
|
|
186
204
|
if (/(?:^|[\s"'=(,]|-[A-Za-z][A-Za-z0-9_-]*:)(?:(?:\\\\){1,2}|\/\/)[^\\/\s"']+[\\/]/.test(checked)) {
|
|
@@ -191,13 +209,16 @@ function validateExplicitFilesystemReferences(command, cwd, reviewedPaths = [])
|
|
|
191
209
|
? checked.replace(/(^|\s)--(?:agent-id|parent-agent-id)\s+\/root(?:\/[a-z][a-z0-9_]*_zaip_agent(?:_[a-z0-9]+)?)+(?=\s|$)/g, "$1--native-agent-id zaip-agent")
|
|
192
210
|
: checked; if (/[\r\n]/.test(checked)) deny("Multiline shell commands are blocked.");
|
|
193
211
|
for (const match of osPathCommand.matchAll(/(["'])(\/(?:etc|home|Users|root|var|opt|private|mnt|proc|sys|dev|tmp)(?:\/[^\r\n]*?)?)\1|(?:^|[\s=(,]|-[A-Za-z][A-Za-z0-9_-]*:)(\/(?:etc|home|Users|root|var|opt|private|mnt|proc|sys|dev|tmp)(?:\/[^\s"'\x60;,)|&]*)?)/gi)) {
|
|
194
|
-
const path = match[2] ?? match[3], before = osPathCommand[match.index - 1] ?? "", after = osPathCommand[match.index + match[0].length] ?? "", jsonPath = /(?:^|\s)'\{"path":$/.test(osPathCommand.slice(0, match.index)) && /^}'(?:\s|$)/.test(osPathCommand.slice(match.index + match[0].length));
|
|
212
|
+
const path = match[2] ?? match[3], before = osPathCommand[match.index - 1] ?? "", after = osPathCommand[match.index + match[0].length] ?? "", jsonPath = /(?:^|\s)'\{"path":$/.test(osPathCommand.slice(0, match.index)) && /^}'(?:\s|$)/.test(osPathCommand.slice(match.index + match[0].length));
|
|
213
|
+
if (principalFullAccess) { await assertCanonicalLiteralPath(path); continue; }
|
|
214
|
+
if (jsonPath) { if (path.includes("\\") || !isWithinRepository(resolve(path))) deny("Explicit filesystem path escapes the repository boundary."); continue; }
|
|
195
215
|
if (path.includes("\\") || !isWithinRepository(resolve(path)) || (match[2] && before && !(/\s/.test(before) || before === "=" || jsonPath)) || (after && !/\s/.test(after) && !jsonPath)) deny("Explicit operating-system paths outside the repository are blocked.");
|
|
196
216
|
}
|
|
197
217
|
if (process.platform === "win32" && /(?:^|[\s"'=(,])\\(?!\\)[^\s\\/"'<>|;]+(?:\\[^\s"'<>|;]+|\.[A-Za-z0-9]{1,10})/.test(checked)) {
|
|
198
218
|
deny("Drive-rooted filesystem paths outside the repository are blocked.");
|
|
199
219
|
}
|
|
200
220
|
for (const candidate of explicitPathCandidates(checked)) {
|
|
221
|
+
if (principalFullAccess) { await assertCanonicalLiteralPath(candidate, cwd); continue; }
|
|
201
222
|
if (process.platform !== "win32" && /^[A-Za-z]:[\\/]/.test(candidate)) deny("Explicit filesystem path escapes the repository boundary.");
|
|
202
223
|
const absolute = resolve(cwd, candidate);
|
|
203
224
|
if (!isWithinRepository(absolute)) {
|
|
@@ -328,7 +349,7 @@ function validateExplicitNetwork(command) {
|
|
|
328
349
|
deny("Malformed network target was rejected.");
|
|
329
350
|
}
|
|
330
351
|
const host = target.hostname.toLowerCase();
|
|
331
|
-
if (!allowedExplicitNetworkHosts.has(host)) {
|
|
352
|
+
if (!principalFullNetwork && !allowedExplicitNetworkHosts.has(host)) {
|
|
332
353
|
deny("Explicit network target is outside the ZAIP allowlist.");
|
|
333
354
|
}
|
|
334
355
|
if (host === "releases.hashicorp.com") {
|
|
@@ -339,188 +360,6 @@ function validateExplicitNetwork(command) {
|
|
|
339
360
|
}
|
|
340
361
|
}
|
|
341
362
|
|
|
342
|
-
function optionMap(tokens) {
|
|
343
|
-
if (tokens.length % 2 !== 0) return null;
|
|
344
|
-
const values = new Map();
|
|
345
|
-
for (let index = 0; index < tokens.length; index += 2) {
|
|
346
|
-
const key = tokens[index];
|
|
347
|
-
const value = tokens[index + 1];
|
|
348
|
-
if (!key.startsWith("--") || value.startsWith("--") || values.has(key)) return null;
|
|
349
|
-
values.set(key, value);
|
|
350
|
-
}
|
|
351
|
-
return values;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function requireExactOptions(options, allowed, required) {
|
|
355
|
-
if ([...options.keys()].some((key) => !allowed.has(key))) return false;
|
|
356
|
-
return [...required].every((key) => typeof options.get(key) === "string" && options.get(key).length > 0);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
function isStringArray(value) {
|
|
360
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string" && item.length > 0);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
async function readCloudContract(options) {
|
|
364
|
-
if (options.get("--contract") !== cloudContractPath) {
|
|
365
|
-
deny("Cloud runner must use the canonical autonomous-cloud contract path.");
|
|
366
|
-
}
|
|
367
|
-
const absolute = resolve(repositoryRoot, cloudContractPath);
|
|
368
|
-
if (!isWithinRepository(absolute)) deny("Cloud runner contract escapes the repository boundary.");
|
|
369
|
-
let contract;
|
|
370
|
-
try {
|
|
371
|
-
contract = JSON.parse(await readFile(absolute, "utf8"));
|
|
372
|
-
} catch {
|
|
373
|
-
deny("Cloud runner contract is absent or malformed.");
|
|
374
|
-
}
|
|
375
|
-
if (
|
|
376
|
-
contract === null ||
|
|
377
|
-
typeof contract !== "object" ||
|
|
378
|
-
contract.version !== 1 ||
|
|
379
|
-
contract.profile !== "autonomous-cloud" ||
|
|
380
|
-
contract.cloudflare === null ||
|
|
381
|
-
typeof contract.cloudflare !== "object" ||
|
|
382
|
-
contract.hostinger === null ||
|
|
383
|
-
typeof contract.hostinger !== "object"
|
|
384
|
-
) {
|
|
385
|
-
deny("Cloud runner contract has no valid autonomous-cloud profile.");
|
|
386
|
-
}
|
|
387
|
-
return contract;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
function cloudflareTargetMatches(contract, options, operation) {
|
|
391
|
-
const cloudflare = contract.cloudflare;
|
|
392
|
-
const record = options.get("--dns-record");
|
|
393
|
-
const managed = isStringArray(cloudflare.dns_records) && cloudflare.dns_records.includes(record);
|
|
394
|
-
const direct = cloudflare.direct_dns_records !== null && typeof cloudflare.direct_dns_records === "object" && Object.hasOwn(cloudflare.direct_dns_records, record);
|
|
395
|
-
return (
|
|
396
|
-
typeof cloudflare.account_id === "string" &&
|
|
397
|
-
typeof cloudflare.zone_id === "string" &&
|
|
398
|
-
isStringArray(cloudflare.allowed_operations) &&
|
|
399
|
-
cloudflare.account_id === options.get("--account-id") &&
|
|
400
|
-
cloudflare.zone_id === options.get("--zone-id") &&
|
|
401
|
-
cloudflare.allowed_operations.includes(operation) &&
|
|
402
|
-
(operation === "cloudflare:dns:read" ? managed || direct : direct && !managed)
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
function hostingerTargetMatches(contract, options, operation) {
|
|
407
|
-
const hostinger = contract.hostinger;
|
|
408
|
-
const role = options.get("--vps-role");
|
|
409
|
-
if (
|
|
410
|
-
typeof hostinger.account_id !== "string" ||
|
|
411
|
-
!isStringArray(hostinger.allowed_operations) ||
|
|
412
|
-
hostinger.account_id !== options.get("--account-id") ||
|
|
413
|
-
!hostinger.allowed_operations.includes(operation) ||
|
|
414
|
-
(role !== "edge" && role !== "application") ||
|
|
415
|
-
hostinger.vps === null ||
|
|
416
|
-
typeof hostinger.vps !== "object"
|
|
417
|
-
) {
|
|
418
|
-
return false;
|
|
419
|
-
}
|
|
420
|
-
const target = hostinger.vps[role];
|
|
421
|
-
if (target === null || typeof target !== "object") return false;
|
|
422
|
-
|
|
423
|
-
if (typeof target.id !== "string" || target.id !== options.get("--vps-id")) return false;
|
|
424
|
-
if (operation.startsWith("hostinger:ssh:")) {
|
|
425
|
-
return typeof target.host_key_sha256 === "string" && target.host_key_sha256 === options.get("--host-key-sha256");
|
|
426
|
-
}
|
|
427
|
-
return true;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
function terraformTargetMatches(contract, options, operation) {
|
|
431
|
-
const terraform = contract.terraform;
|
|
432
|
-
if (
|
|
433
|
-
terraform === null ||
|
|
434
|
-
typeof terraform !== "object" ||
|
|
435
|
-
!isStringArray(terraform.allowed_operations) ||
|
|
436
|
-
!terraform.allowed_operations.includes(operation)
|
|
437
|
-
) return false;
|
|
438
|
-
if (operation === "terraform:plan") return terraform.working_directory === options.get("--terraform-directory");
|
|
439
|
-
if (operation === "terraform:apply") {
|
|
440
|
-
const plan = terraform.plan;
|
|
441
|
-
return plan !== null && typeof plan === "object" && typeof plan.path === "string" && typeof plan.sha256 === "string" && typeof plan.policy_result_sha256 === "string" && plan.path === options.get("--plan-path") && plan.sha256 === options.get("--plan-sha256") && plan.policy_result_sha256 === options.get("--policy-result-sha256");
|
|
442
|
-
}
|
|
443
|
-
return terraform.import_targets !== null && typeof terraform.import_targets === "object" && terraform.import_targets[options.get("--resource-address")] === options.get("--resource-id");
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
function validateDirectCloudProviderMutation(command) {
|
|
447
|
-
const tokens = tokenizeLiteralCommand(command);
|
|
448
|
-
if (/^(?:node|node\.exe)$/i.test(tokens?.[0] ?? "") && tokens?.[1] === cloudRunnerScript) return;
|
|
449
|
-
if (/\bterraform(?:\.exe)?\s+(?:apply|destroy|import)\b/i.test(command)) deny("Direct Terraform mutation is blocked; use the contract-bound cloud runner.");
|
|
450
|
-
const provider = /^(?:wrangler|cloudflare|hostinger)(?:\.exe|\.cmd|\.js|\.mjs)?$/i;
|
|
451
|
-
const wrapperProvider = /^(?:wrangler|cloudflare|hostinger)(?:\.exe|\.cmd|\.js|\.mjs|@[A-Za-z0-9][A-Za-z0-9._-]*)?$/i;
|
|
452
|
-
const base = (token) => token?.split(/[\\/]/).at(-1) ?? "";
|
|
453
|
-
if (tokens === null) {
|
|
454
|
-
// Complex shell syntax stays fail-closed; literal arguments are inspected as tokens below.
|
|
455
|
-
if (/\b(?:wrangler|cloudflare|hostinger)(?:\.exe)?\b/i.test(command)) deny("Direct Cloudflare or Hostinger commands are blocked; use the contract-bound cloud runner.");
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
let index = 0;
|
|
459
|
-
const launcher = base(tokens[0]);
|
|
460
|
-
if (/^(?:npx|pnpm|npm|yarn|bunx)(?:\.cmd|\.exe)?$/i.test(launcher)) {
|
|
461
|
-
// Package-manager options can move the executable. Inspect exact provider
|
|
462
|
-
// tokens across this recognized wrapper; paths and document names remain inert.
|
|
463
|
-
if (tokens.slice(1).some((token) => wrapperProvider.test(token))) deny("Direct Cloudflare or Hostinger commands are blocked; use the contract-bound cloud runner.");
|
|
464
|
-
index = 1;
|
|
465
|
-
if (/^npx(?:\.cmd|\.exe)?$/i.test(launcher) && /^(?:--yes|-y)$/.test(tokens[index] ?? "")) index += 1;
|
|
466
|
-
if (/^pnpm(?:\.cmd|\.exe)?$/i.test(launcher) && /^(?:--filter|--dir)$/.test(tokens[index] ?? "") && tokens[index + 1]) index += 2;
|
|
467
|
-
if (/^(?:exec|dlx)$/.test(tokens[index] ?? "")) index += 1;
|
|
468
|
-
if (tokens[index] === "--") index += 1;
|
|
469
|
-
if (/^(?:exec|dlx)$/.test(tokens[index] ?? "")) index += 1;
|
|
470
|
-
} else if (/^(?:node|node\.exe)$/i.test(launcher)) index = 1;
|
|
471
|
-
else if (/^(?:powershell|pwsh|cmd|bash|sh)(?:\.exe)?$/i.test(launcher)) {
|
|
472
|
-
if (/\b(?:wrangler|cloudflare|hostinger)(?:\.exe)?\b/i.test(command)) deny("Direct Cloudflare or Hostinger commands are blocked; use the contract-bound cloud runner.");
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
if (provider.test(base(tokens[index]))) deny("Direct Cloudflare or Hostinger commands are blocked; use the contract-bound cloud runner.");
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
async function validateCloudRunner(command) {
|
|
479
|
-
const tokens = tokenizeLiteralCommand(command);
|
|
480
|
-
if (tokens === null || tokens.length < 2 || !/^(?:node|node\.exe)$/i.test(tokens[0]) || tokens[1] !== cloudRunnerScript) {
|
|
481
|
-
return false;
|
|
482
|
-
}
|
|
483
|
-
const options = optionMap(tokens.slice(2));
|
|
484
|
-
if (options === null) deny("Cloud runner requires literal, named arguments.");
|
|
485
|
-
const operation = options.get("--operation");
|
|
486
|
-
if (operation === "contract:derive") {
|
|
487
|
-
if (!requireExactOptions(options, new Set(["--operation", "--credentials-source"]), new Set(["--operation", "--credentials-source"]))) {
|
|
488
|
-
deny("Cloud contract derivation has unrecognized arguments.");
|
|
489
|
-
}
|
|
490
|
-
if (options.get("--credentials-source") !== "root") {
|
|
491
|
-
deny("Cloud contract derivation requires the selected root credential source.");
|
|
492
|
-
}
|
|
493
|
-
return true;
|
|
494
|
-
}
|
|
495
|
-
const provider = cloudOperationProviders.get(operation);
|
|
496
|
-
if (provider === undefined) deny("Cloud runner operation is unrecognized or destructive.");
|
|
497
|
-
const common = new Set(["--contract", "--operation", "--provider", "--account-id", "--credentials-source", "--zone-id", "--dns-record", "--vps-role", "--vps-id", "--host-key-sha256", "--terraform-directory", "--plan-path", "--plan-sha256", "--policy-result-sha256", "--resource-address", "--resource-id"]);
|
|
498
|
-
const baseRequired = provider === "terraform"
|
|
499
|
-
? new Set(["--contract", "--operation", "--credentials-source"])
|
|
500
|
-
: new Set(["--contract", "--operation", "--provider", "--account-id", "--credentials-source"]);
|
|
501
|
-
if (!requireExactOptions(options, common, baseRequired)) {
|
|
502
|
-
deny("Cloud runner invocation has unrecognized arguments.");
|
|
503
|
-
}
|
|
504
|
-
if ((provider !== "terraform" && options.get("--provider") !== provider) || !["root", "agents"].includes(options.get("--credentials-source"))) {
|
|
505
|
-
deny("Cloud runner provider or credential source is outside the approved contract.");
|
|
506
|
-
}
|
|
507
|
-
const contract = await readCloudContract(options);
|
|
508
|
-
const valid = provider === "cloudflare"
|
|
509
|
-
? requireExactOptions(options, common, new Set(["--contract", "--operation", "--provider", "--account-id", "--credentials-source", "--zone-id", "--dns-record"])) && cloudflareTargetMatches(contract, options, operation)
|
|
510
|
-
: provider === "hostinger"
|
|
511
|
-
? requireExactOptions(options, common, new Set(["--contract", "--operation", "--provider", "--account-id", "--credentials-source", "--vps-role"])) && hostingerTargetMatches(contract, options, operation)
|
|
512
|
-
: requireExactOptions(
|
|
513
|
-
options,
|
|
514
|
-
common,
|
|
515
|
-
operation === "terraform:plan"
|
|
516
|
-
? new Set(["--contract", "--operation", "--credentials-source", "--terraform-directory"])
|
|
517
|
-
: operation === "terraform:apply"
|
|
518
|
-
? new Set(["--contract", "--operation", "--credentials-source", "--plan-path", "--plan-sha256", "--policy-result-sha256"])
|
|
519
|
-
: new Set(["--contract", "--operation", "--credentials-source", "--resource-address", "--resource-id"]),
|
|
520
|
-
) && terraformTargetMatches(contract, options, operation);
|
|
521
|
-
if (!valid) deny("Cloud runner target, operation, or host identity is outside the approved contract.");
|
|
522
|
-
return true;
|
|
523
|
-
}
|
|
524
363
|
|
|
525
364
|
${CODEX_STALE_GIT_LOCK_HELPER}
|
|
526
365
|
|
|
@@ -597,6 +436,10 @@ async function validateShellDeletion(command, cwd) {
|
|
|
597
436
|
}
|
|
598
437
|
|
|
599
438
|
async function validatePatch(command) {
|
|
439
|
+
for (const match of command.matchAll(/^\*\*\* (?:Add|Update|Delete) File:\s*(.+)$|^\*\*\* Move to:\s*(.+)$/gm)) {
|
|
440
|
+
const rawPath = (match[1] ?? match[2]).trim().replace(/^["']|["']$/g, "");
|
|
441
|
+
if (principalFullAccess) await assertCanonicalLiteralPath(rawPath);
|
|
442
|
+
}
|
|
600
443
|
const paths = patchPaths(command);
|
|
601
444
|
const lifecycleRecordTouched = paths.some((path) =>
|
|
602
445
|
/^\.agents[\\/]records[\\/](?:prds|specs|tasks)[\\/]/i.test(path));
|
|
@@ -605,8 +448,10 @@ async function validatePatch(command) {
|
|
|
605
448
|
}
|
|
606
449
|
for (const path of paths) {
|
|
607
450
|
const absolute = resolve(repositoryRoot, path);
|
|
608
|
-
if (
|
|
609
|
-
if (!
|
|
451
|
+
if (credentialReferencePattern.test(absolute)) deny("Direct credential and secret access is blocked.");
|
|
452
|
+
if (!isWithinRepository(absolute) && !principalFullAccess) deny("Patch path escapes the repository boundary.");
|
|
453
|
+
if (principalFullAccess) await assertCanonicalLiteralPath(absolute);
|
|
454
|
+
else if (!await nativeAuthority?.isSafePatchPath(absolute, repositoryRoot)) deny("Patch path traverses a link or could not be safely verified.");
|
|
610
455
|
if (repositoryPath(absolute) === "") deny("Patching the repository root is blocked.");
|
|
611
456
|
if (protectedTaskWorkspacePath(repositoryPath(absolute))) deny("Task workspace claims are managed only by the canonical CLI.");
|
|
612
457
|
if (isGitPath(absolute)) {
|
|
@@ -628,13 +473,25 @@ async function requiresOrchestratorRun(event, command) {
|
|
|
628
473
|
return paths.length === 0 || paths.some((path) => !lifecycleExemptPathPattern.test(path));
|
|
629
474
|
}
|
|
630
475
|
if (event.tool_name === "Edit" || event.tool_name === "Write") {
|
|
631
|
-
if (
|
|
476
|
+
if (principalFullAccess) await assertCanonicalLiteralPath(command, event.cwd ?? repositoryRoot);
|
|
477
|
+
else if (!await nativeAuthority?.isSafePatchPath(resolve(event.cwd ?? repositoryRoot, command), repositoryRoot)) deny("File write path traverses a link or could not be safely verified.");
|
|
632
478
|
const path = repositoryPath(resolve(event.cwd ?? repositoryRoot, command));
|
|
633
479
|
if (protectedTaskWorkspacePath(path)) deny("Task workspace claims are managed only by the canonical CLI.");
|
|
634
480
|
return !lifecycleExemptPathPattern.test(path);
|
|
635
481
|
}
|
|
636
482
|
// Only single literal writes claim exemptions; compound commands remain gated.
|
|
637
483
|
const tokens = tokenizeLiteralCommand(command);
|
|
484
|
+
// The public CLI validates the immutable post-publication receipt, observed
|
|
485
|
+
// invocation, completed maintenance Gui and exact target before any write.
|
|
486
|
+
// Its first call necessarily follows the maintenance root's terminal Stop.
|
|
487
|
+
if (tokens && !Object.values(shellSyntax(command)).some(Boolean) &&
|
|
488
|
+
/^(?:node|node\.exe)$/i.test(tokens[0] ?? "") &&
|
|
489
|
+
/^\.agents[\\/]runtime[\\/]bin[\\/]zaip\.mjs$/.test(tokens[1] ?? "") &&
|
|
490
|
+
tokens[2] === "task" && tokens[3] === "resume-post-publication" &&
|
|
491
|
+
/^TASK-[A-Z0-9-]+$/.test(tokens[4] ?? "") && tokens[5] === "--receipt" &&
|
|
492
|
+
/^\.local[\\/]recovery[\\/]post-publication[\\/][a-f0-9]{64}\.json$/.test(tokens[6] ?? "") &&
|
|
493
|
+
(tokens.length === 7 || (tokens.length === 8 && tokens[7] === "--json"))) return false;
|
|
494
|
+
if (tokens && tokens[2] === "task" && tokens[3] === "resume-post-publication") return true;
|
|
638
495
|
if (tokens && /^(?:Set-Content|Add-Content|Out-File|New-Item|Copy-Item|Move-Item|Remove-Item|Rename-Item|sc|ac|ni|cp|mv|mi|ri|rm|git(?:\.exe)?)$/i.test(tokens[0] ?? "") &&
|
|
639
496
|
tokens.some(token => token.toLowerCase().includes("task-workspaces") &&
|
|
640
497
|
protectedTaskWorkspacePath(repositoryPath(resolve(event.cwd ?? repositoryRoot, token)))))
|
|
@@ -649,7 +506,8 @@ async function requiresOrchestratorRun(event, command) {
|
|
|
649
506
|
}
|
|
650
507
|
if (valid && options.has("-literalpath") && options.has("-value")) {
|
|
651
508
|
const path = repositoryPath(resolve(event.cwd ?? repositoryRoot, options.get("-literalpath")));
|
|
652
|
-
if (
|
|
509
|
+
if (principalFullAccess) await assertCanonicalLiteralPath(options.get("-literalpath"), event.cwd ?? repositoryRoot);
|
|
510
|
+
else if (!await nativeAuthority?.isSafePatchPath(resolve(repositoryRoot, path), repositoryRoot)) deny("Shell write path traverses a link or could not be safely verified.");
|
|
653
511
|
if (protectedTaskWorkspacePath(path)) deny("Task workspace claims are managed only by the canonical CLI.");
|
|
654
512
|
if (lifecycleExemptPathPattern.test(path)) return false;
|
|
655
513
|
}
|
|
@@ -664,37 +522,65 @@ async function requiresOrchestratorRun(event, command) {
|
|
|
664
522
|
try { optIn = JSON.parse(await readFile(resolve(repositoryRoot, ".local/validation-runtime.json"), "utf8")); }
|
|
665
523
|
catch { optIn = null; }
|
|
666
524
|
const reviewed = await (nativeAuthority?.reviewedValidationRuntimePaths?.(command, tokenizeLiteralCommand, repositoryRoot) ?? []);
|
|
667
|
-
if (optIn?.executable ===
|
|
525
|
+
if (typeof optIn?.executable === "string" &&
|
|
526
|
+
tokens[0].replaceAll("/", "\\").toLowerCase() === optIn.executable.replaceAll("/", "\\").toLowerCase() &&
|
|
527
|
+
reviewed.length === 1 && reviewed[0] === tokens[0]) return false;
|
|
668
528
|
}
|
|
669
529
|
return requiresShellMutationRun(command, tokens, event.cwd ?? repositoryRoot, repositoryRoot);
|
|
670
530
|
}
|
|
671
531
|
|
|
672
|
-
|
|
673
|
-
const info = await lstat(path);
|
|
674
|
-
if (!info.isFile() || info.isSymbolicLink() || info.size > maximumBytes) throw new Error("Unsafe Run lookup file");
|
|
675
|
-
const physical = await realpath(path);
|
|
676
|
-
const identity = value => process.platform === "win32" ? value.toLowerCase() : value;
|
|
677
|
-
if (!inside(physical, authorityRoot) || identity(physical) !== identity(resolve(path))) throw new Error("Run lookup must not follow links");
|
|
678
|
-
return readFile(path, "utf8");
|
|
679
|
-
}
|
|
532
|
+
const boundedCanonicalText = (path, maximumBytes, authorityRoot = repositoryRoot) => readBoundedCanonicalText(path, maximumBytes, authorityRoot);
|
|
680
533
|
|
|
681
534
|
let raw = "";
|
|
682
535
|
for await (const chunk of process.stdin) raw += chunk;
|
|
683
536
|
|
|
684
537
|
let event;
|
|
685
|
-
|
|
686
|
-
event = JSON.parse(raw);
|
|
687
|
-
} catch {
|
|
688
|
-
deny("Malformed hook input was rejected.");
|
|
689
|
-
}
|
|
538
|
+
event = parseHookEvent(raw, deny);
|
|
690
539
|
|
|
691
540
|
const toolInput = event?.tool_input;
|
|
692
|
-
|
|
541
|
+
const candidateWorkdir = typeof toolInput?.workdir === "string" && isAbsolute(toolInput.workdir) ? toolInput.workdir : event.cwd;
|
|
542
|
+
try { repositoryRoot = await selectHookRepositoryRoot(installedRoot, candidateWorkdir); }
|
|
543
|
+
catch { repositoryRoot = installedRoot; }
|
|
544
|
+
if (typeof activation?.hookPolicies !== "function") {
|
|
545
|
+
let projectConfig;
|
|
546
|
+
try {
|
|
547
|
+
projectConfig = await readBoundedCanonicalText(resolve(repositoryRoot, ".agents/config/project.yaml"), 65536, repositoryRoot);
|
|
548
|
+
} catch (error) {
|
|
549
|
+
if (error?.code === "ENOENT") { writeSync(1, "{}\n"); process.exit(0); }
|
|
550
|
+
deny("ZAIP project policy cannot be verified without hook activation.");
|
|
551
|
+
}
|
|
552
|
+
const sections = [...projectConfig.matchAll(/^zaip:([^\r\n]*)$/gm)];
|
|
553
|
+
const inlineDisabled = sections.length === 1 && /^\s*\{\s*enabled:\s*false\s*\}(?:\s*#.*)?$/.test(sections[0][1]);
|
|
554
|
+
const following = sections.length === 1 ? projectConfig.slice(sections[0].index + sections[0][0].length).split(/(?=^[^\s#])/m)[0] : "";
|
|
555
|
+
const enabledEntries = following.match(/^\s+enabled:/gm) ?? [];
|
|
556
|
+
const blockDisabled = sections.length === 1 && /^\s*(?:#.*)?$/.test(sections[0][1]) && enabledEntries.length === 1 && /^\s+enabled:\s*false(?:\s*#.*)?$/m.test(following);
|
|
557
|
+
if (inlineDisabled || blockDisabled) { writeSync(1, "{}\n"); process.exit(0); }
|
|
558
|
+
deny("ZAIP hook activation module is unavailable for an enabled or invalid project policy.");
|
|
559
|
+
}
|
|
560
|
+
const hookPolicy = await activation.hookPolicies(repositoryRoot, (path, maximumBytes) => readBoundedCanonicalText(path, maximumBytes, repositoryRoot));
|
|
561
|
+
activation?.recordPolicyDiagnostic?.(repositoryRoot, hookPolicy);
|
|
562
|
+
if (hookPolicy.tracking) activation?.recordHookObservation?.(repositoryRoot, event, hookPolicy.governance);
|
|
563
|
+
if (!hookPolicy.governance) {
|
|
564
|
+
if (hookPolicy.diagnostic && !["config_missing", "policy_missing"].includes(hookPolicy.diagnostic))
|
|
565
|
+
deny("ZAIP project policy is invalid or unavailable; restore the canonical activation and project configuration.");
|
|
566
|
+
writeSync(1, "{}\n");
|
|
567
|
+
process.exit(0);
|
|
568
|
+
}
|
|
569
|
+
const configuredPolicy = await configuredPermissionProfile(repositoryRoot);
|
|
570
|
+
const principal = [event.agent_id, event.agent_type, event.agent_path, event.parent_agent_id].every(value => value === undefined) && event.permission_mode !== "plan";
|
|
571
|
+
principalFullAccess = principal && configuredPolicy.filesystem;
|
|
572
|
+
principalFullNetwork = principal && configuredPolicy.network;
|
|
573
|
+
try { const scope = await selectHookExecutionWorkspace(installedRoot, event, principalFullAccess); repositoryRoot = scope.repositoryRoot; event = { ...event, cwd: scope.cwd }; }
|
|
693
574
|
catch (error) { deny(error.message); }
|
|
694
|
-
if (
|
|
575
|
+
if (credentialReferencePattern.test(event.cwd)) deny("Direct credential and secret access is blocked.");
|
|
576
|
+
if (!principalFullAccess && !isWithinRepository(event.cwd)) {
|
|
695
577
|
deny("Tool execution outside the ZAIP-AIOS repository is blocked.");
|
|
696
578
|
}
|
|
697
579
|
|
|
580
|
+
const { validateCloudRunner, validateDirectCloudProviderMutation } = createCloudValidators({
|
|
581
|
+
tokenizeLiteralCommand, deny, repositoryRoot, isWithinRepository, cloudRunnerScript, cloudContractPath, cloudOperationProviders,
|
|
582
|
+
});
|
|
583
|
+
|
|
698
584
|
const commandFromCmd = toolInput?.cmd;
|
|
699
585
|
const commandFromCommand = toolInput?.command;
|
|
700
586
|
if (typeof commandFromCmd === "string" && typeof commandFromCommand === "string" && commandFromCmd !== commandFromCommand) {
|
|
@@ -703,7 +589,7 @@ if (typeof commandFromCmd === "string" && typeof commandFromCommand === "string"
|
|
|
703
589
|
const command = typeof commandFromCmd === "string" ? commandFromCmd : commandFromCommand;
|
|
704
590
|
if (typeof command !== "string") deny("A matched tool call without a command was rejected.");
|
|
705
591
|
|
|
706
|
-
if (await requiresOrchestratorRun(event, command)
|
|
592
|
+
if (await requiresOrchestratorRun(event, command)) {
|
|
707
593
|
const directRun = await nativeAuthority?.hasCurrentTaskOrchestratorRun(event, installedRoot, boundedCanonicalText);
|
|
708
594
|
const primaryRead = (path, maximumBytes) => boundedCanonicalText(path, maximumBytes, installedRoot);
|
|
709
595
|
const delegated = directRun ? false : await nativeAuthority?.hasNativeDelegatedPatch(event, repositoryRoot, primaryRead, installedRoot);
|
|
@@ -722,7 +608,7 @@ else {
|
|
|
722
608
|
...await reviewedReleasePaths(command, event.cwd, repositoryRoot, credentialReferencePattern, deny),
|
|
723
609
|
...await reviewedContextReadPaths(command, credentialReferencePattern, installedRoot), ...await (nativeAuthority?.reviewedValidationRuntimePaths?.(command, tokenizeLiteralCommand, repositoryRoot) ?? []),
|
|
724
610
|
];
|
|
725
|
-
validateExplicitFilesystemReferences(command, event.cwd, reviewedPaths);
|
|
611
|
+
await validateExplicitFilesystemReferences(command, event.cwd, reviewedPaths);
|
|
726
612
|
validateInlineExecution(command, deny);
|
|
727
613
|
if (!isLiteralReadSequence(command)) {
|
|
728
614
|
await validateGitPublication(command);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-pre-tool-hook-template.js","sourceRoot":"","sources":["../../src/services/codex-pre-tool-hook-template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AAEjF,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"codex-pre-tool-hook-template.js","sourceRoot":"","sources":["../../src/services/codex-pre-tool-hook-template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AAEjF,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0W3C,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqQ5B,CAAC"}
|
|
@@ -3,22 +3,21 @@ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { homedir, tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { afterEach, expect, it } from "vitest";
|
|
6
|
-
import {
|
|
7
|
-
import { CODEX_NATIVE_TOOL_AUTHORITY_HOOK } from "./codex-native-tool-authority-template.js";
|
|
8
|
-
import { CODEX_PRE_TOOL_HOOK } from "./codex-pre-tool-hook-template.js";
|
|
9
|
-
import { CODEX_PRE_TOOL_SUPPORT_HOOK } from "./codex-pre-tool-support-template.js";
|
|
6
|
+
import { installPreToolHook } from "./codex-pre-tool-hook-fixture.test-support.js";
|
|
10
7
|
const roots = [];
|
|
8
|
+
async function enableZaip(root) {
|
|
9
|
+
const config = join(root, ".agents", "config");
|
|
10
|
+
await mkdir(config, { recursive: true });
|
|
11
|
+
await writeFile(join(config, "project.yaml"), "zaip:\n enabled: true\ntracking:\n enabled: false\n");
|
|
12
|
+
}
|
|
11
13
|
afterEach(async () => {
|
|
12
14
|
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
13
15
|
});
|
|
14
16
|
it("portable hook restricts HashiCorp release reads to the exact HTTPS host and GET or HEAD", async () => {
|
|
15
17
|
const root = await mkdtemp(join(tmpdir(), "zaip-cli-hashicorp-read-"));
|
|
16
18
|
roots.push(root);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
await writeFile(join(hooks, "zaip-pre-tool-use.mjs"), CODEX_PRE_TOOL_HOOK);
|
|
20
|
-
await writeFile(join(hooks, "zaip-pre-tool-support.mjs"), CODEX_PRE_TOOL_SUPPORT_HOOK);
|
|
21
|
-
await writeFile(join(hooks, "zaip-native-tool-authority.mjs"), CODEX_NATIVE_TOOL_AUTHORITY_HOOK);
|
|
19
|
+
await enableZaip(root);
|
|
20
|
+
const { hooks } = await installPreToolHook(root);
|
|
22
21
|
const invoke = (cmd) => spawnSync(process.execPath, [join(hooks, "zaip-pre-tool-use.mjs")], {
|
|
23
22
|
cwd: root,
|
|
24
23
|
encoding: "utf8",
|
|
@@ -50,12 +49,7 @@ it("portable hook restricts HashiCorp release reads to the exact HTTPS host and
|
|
|
50
49
|
it("portable hook releases Task authority only for literal ZAIP disable", async () => {
|
|
51
50
|
const root = await mkdtemp(join(tmpdir(), "zaip-cli-disabled-hook-"));
|
|
52
51
|
roots.push(root);
|
|
53
|
-
const hooks =
|
|
54
|
-
await mkdir(hooks, { recursive: true });
|
|
55
|
-
await writeFile(join(hooks, "zaip-pre-tool-use.mjs"), CODEX_PRE_TOOL_HOOK);
|
|
56
|
-
await writeFile(join(hooks, "zaip-hook-activation.mjs"), CODEX_HOOK_ACTIVATION);
|
|
57
|
-
await writeFile(join(hooks, "zaip-pre-tool-support.mjs"), CODEX_PRE_TOOL_SUPPORT_HOOK);
|
|
58
|
-
await writeFile(join(hooks, "zaip-native-tool-authority.mjs"), CODEX_NATIVE_TOOL_AUTHORITY_HOOK);
|
|
52
|
+
const { hooks } = await installPreToolHook(root);
|
|
59
53
|
const config = join(root, ".agents", "config", "project.yaml");
|
|
60
54
|
await mkdir(join(root, ".agents", "config"), { recursive: true });
|
|
61
55
|
const run = () => spawnSync(process.execPath, [join(hooks, "zaip-pre-tool-use.mjs")], {
|
|
@@ -68,10 +62,11 @@ it("portable hook releases Task authority only for literal ZAIP disable", async
|
|
|
68
62
|
}),
|
|
69
63
|
});
|
|
70
64
|
await writeFile(config, "schema_version: 1\nzaip:\n enabled: false\n");
|
|
71
|
-
expect(run().stdout).toBe("");
|
|
65
|
+
expect(run().stdout).toBe("{}\n");
|
|
72
66
|
await writeFile(config, 'schema_version: 1\nzaip:\n enabled: "false"\n');
|
|
73
67
|
expect(JSON.parse(run().stdout).hookSpecificOutput.permissionDecision).toBe("deny");
|
|
74
68
|
await writeFile(config, "schema_version: 1\nzaip:\n enabled: false\n");
|
|
69
|
+
await writeFile(config, "schema_version: 1\nzaip:\n enabled: true\n");
|
|
75
70
|
await rm(join(hooks, "zaip-hook-activation.mjs"));
|
|
76
71
|
for (const state of ["missing", "legacy"]) {
|
|
77
72
|
if (state === "legacy")
|
|
@@ -79,17 +74,14 @@ it("portable hook releases Task authority only for literal ZAIP disable", async
|
|
|
79
74
|
const result = run();
|
|
80
75
|
expect(result.status).toBe(0);
|
|
81
76
|
expect(result.stderr).toBe("");
|
|
82
|
-
expect(JSON.parse(result.stdout).hookSpecificOutput
|
|
77
|
+
expect(JSON.parse(result.stdout).hookSpecificOutput?.permissionDecision).toBe("deny");
|
|
83
78
|
}
|
|
84
79
|
});
|
|
85
80
|
it("permits only literal reviewed release commands with controlled external paths", async () => {
|
|
86
81
|
const root = await mkdtemp(join(tmpdir(), "zaip-cli-release-hook-"));
|
|
87
82
|
roots.push(root);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
await writeFile(hookPath, CODEX_PRE_TOOL_HOOK);
|
|
91
|
-
await writeFile(join(root, ".codex", "hooks", "zaip-pre-tool-support.mjs"), CODEX_PRE_TOOL_SUPPORT_HOOK);
|
|
92
|
-
await writeFile(join(root, ".codex", "hooks", "zaip-native-tool-authority.mjs"), CODEX_NATIVE_TOOL_AUTHORITY_HOOK);
|
|
83
|
+
await enableZaip(root);
|
|
84
|
+
const { hookPath } = await installPreToolHook(root);
|
|
93
85
|
await mkdir(join(root, "scripts"), { recursive: true });
|
|
94
86
|
await writeFile(join(root, "scripts", "local-release-preparation.mjs"), "export default null;\n");
|
|
95
87
|
await writeFile(join(root, "scripts", "local-release-deploy.mjs"), "export default null;\n");
|
|
@@ -160,11 +152,8 @@ it("permits only literal reviewed release commands with controlled external path
|
|
|
160
152
|
it("uses tool_input.cmd, rejects conflicting commands, and keeps read-only git formatting", async () => {
|
|
161
153
|
const root = await mkdtemp(join(tmpdir(), "zaip-cli-pre-tool-"));
|
|
162
154
|
roots.push(root);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
await writeFile(hookPath, CODEX_PRE_TOOL_HOOK);
|
|
166
|
-
await writeFile(join(root, ".codex", "hooks", "zaip-pre-tool-support.mjs"), CODEX_PRE_TOOL_SUPPORT_HOOK);
|
|
167
|
-
await writeFile(join(root, ".codex", "hooks", "zaip-native-tool-authority.mjs"), CODEX_NATIVE_TOOL_AUTHORITY_HOOK);
|
|
155
|
+
await enableZaip(root);
|
|
156
|
+
const { hookPath } = await installPreToolHook(root);
|
|
168
157
|
const invoke = (tool_input) => spawnSync(process.execPath, [hookPath], {
|
|
169
158
|
cwd: root,
|
|
170
159
|
encoding: "utf8",
|
|
@@ -306,6 +295,22 @@ it("uses tool_input.cmd, rejects conflicting commands, and keeps read-only git f
|
|
|
306
295
|
expect(JSON.parse(result.stdout).hookSpecificOutput.permissionDecision).toBe("deny");
|
|
307
296
|
expect(JSON.parse(result.stdout).hookSpecificOutput.permissionDecisionReason).toContain(reason);
|
|
308
297
|
}
|
|
298
|
+
const metadataAndSearch = invoke({
|
|
299
|
+
cmd: "Get-Item package.json | Select-Object FullName; rg -n 'spawn|execPath|node|runCli' packages/cli/src/index.ts | Select-Object -First 22",
|
|
300
|
+
});
|
|
301
|
+
expect(metadataAndSearch.status).toBe(0);
|
|
302
|
+
expect(metadataAndSearch.stdout).toBe("");
|
|
303
|
+
const originalRead = invoke({
|
|
304
|
+
cmd: "Get-Item -LiteralPath node_modules/vitest | Select-Object -ExpandProperty Target; rg -n 'spawn|execPath|node|runCli' packages/cli/src -g 'prd-spec-composite-create*.ts' -g 'cli-test-harness.ts' | Select-Object -First 22",
|
|
305
|
+
});
|
|
306
|
+
expect(originalRead.status).toBe(0);
|
|
307
|
+
expect(originalRead.stdout).toBe("");
|
|
308
|
+
const realInterpreterPipe = invoke({
|
|
309
|
+
cmd: "rg -n 'spawn|execPath|node|runCli' packages/cli/src/index.ts | node",
|
|
310
|
+
});
|
|
311
|
+
expect(JSON.parse(realInterpreterPipe.stdout).hookSpecificOutput.permissionDecisionReason).toContain("Inline or piped interpreter execution");
|
|
312
|
+
expect(JSON.parse(invoke({ cmd: "Get-Item Env:SECRET | Select-Object FullName" }).stdout)
|
|
313
|
+
.hookSpecificOutput.permissionDecision).toBe("deny");
|
|
309
314
|
const denied = invoke({ cmd: "git status", command: "git reset --hard" });
|
|
310
315
|
expect(denied.status).toBe(0);
|
|
311
316
|
expect(JSON.parse(denied.stdout).hookSpecificOutput.permissionDecision).toBe("deny");
|
|
@@ -314,57 +319,4 @@ it("uses tool_input.cmd, rejects conflicting commands, and keeps read-only git f
|
|
|
314
319
|
const nestedDiskDenied = invoke({ cmd: "cmd /c " + "format" + " C:" });
|
|
315
320
|
expect(JSON.parse(nestedDiskDenied.stdout).hookSpecificOutput.permissionDecision).toBe("deny");
|
|
316
321
|
});
|
|
317
|
-
it("allows explicit acceptance arguments before Task capture without allowing writes", async () => {
|
|
318
|
-
const root = await mkdtemp(join(tmpdir(), "zaip-cli-bootstrap-hook-"));
|
|
319
|
-
roots.push(root);
|
|
320
|
-
const hooks = join(root, ".codex", "hooks");
|
|
321
|
-
await mkdir(hooks, { recursive: true });
|
|
322
|
-
await writeFile(join(hooks, "zaip-pre-tool-use.mjs"), CODEX_PRE_TOOL_HOOK);
|
|
323
|
-
await writeFile(join(hooks, "zaip-pre-tool-support.mjs"), CODEX_PRE_TOOL_SUPPORT_HOOK);
|
|
324
|
-
await writeFile(join(hooks, "zaip-native-tool-authority.mjs"), CODEX_NATIVE_TOOL_AUTHORITY_HOOK);
|
|
325
|
-
const invoke = (cmd) => spawnSync(process.execPath, [join(hooks, "zaip-pre-tool-use.mjs")], {
|
|
326
|
-
cwd: root,
|
|
327
|
-
encoding: "utf8",
|
|
328
|
-
windowsHide: true,
|
|
329
|
-
env: { ...process.env, CODEX_THREAD_ID: "bootstrap-thread", CODEX_TURN_ID: "bootstrap-turn" },
|
|
330
|
-
input: JSON.stringify({
|
|
331
|
-
cwd: root,
|
|
332
|
-
session_id: "bootstrap-thread",
|
|
333
|
-
turn_id: "bootstrap-turn",
|
|
334
|
-
tool_use_id: "bootstrap-tool",
|
|
335
|
-
tool_name: "exec_command",
|
|
336
|
-
tool_input: { cmd },
|
|
337
|
-
}),
|
|
338
|
-
});
|
|
339
|
-
const result = invoke("node .agents/runtime/bin/zaip.mjs conversation start --intent new-work --acceptance AC-WORK-001-09 --json");
|
|
340
|
-
expect(result.status, result.stderr).toBe(0);
|
|
341
|
-
const output = JSON.parse(result.stdout).hookSpecificOutput;
|
|
342
|
-
expect(output.permissionDecision, result.stdout).toBe("allow");
|
|
343
|
-
expect(output.updatedInput.cmd).toContain("--codex-hook-id=");
|
|
344
|
-
for (const cmd of [
|
|
345
|
-
"ac src/a.ts payload",
|
|
346
|
-
"AC src/a.ts payload",
|
|
347
|
-
"(Set-Content -LiteralPath src/a.ts -Value code)",
|
|
348
|
-
"Write-Output (Set-Content -LiteralPath src/a.ts -Value code)",
|
|
349
|
-
"env cp src/a.ts src/b.ts",
|
|
350
|
-
"exec cp src/a.ts src/b.ts",
|
|
351
|
-
"FOO=1 cp src/a.ts src/b.ts",
|
|
352
|
-
"unknown-wrapper ac src/a.ts payload",
|
|
353
|
-
".\\echo.cmd ac src/a.ts payload",
|
|
354
|
-
".\\node.cmd .agents/runtime/bin/zaip.mjs conversation start --acceptance AC-WORK-001-09",
|
|
355
|
-
"echo.cmd ac src/a.ts payload",
|
|
356
|
-
"node.cmd .agents/runtime/bin/zaip.mjs conversation start --acceptance AC-WORK-001-09",
|
|
357
|
-
"node scripts/unknown.mjs --acceptance AC-WORK-001-09",
|
|
358
|
-
"command ac src/a.ts payload",
|
|
359
|
-
".\\sed.exe -i s/a/b/ src/a.ts",
|
|
360
|
-
".\\sed.exe -f script.sed src/input.ts",
|
|
361
|
-
"git apply patch.diff",
|
|
362
|
-
"node scripts/read.mjs --acceptance AC-WORK-001-09; ac src/a.ts payload",
|
|
363
|
-
"node scripts/read.mjs > src/a.ts",
|
|
364
|
-
]) {
|
|
365
|
-
const blocked = invoke(cmd);
|
|
366
|
-
expect(blocked.status, blocked.stderr).toBe(0);
|
|
367
|
-
expect(JSON.parse(blocked.stdout).hookSpecificOutput.permissionDecision).toBe("deny");
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
322
|
//# sourceMappingURL=codex-pre-tool-hook-template.test.js.map
|