@zaip-ai/cli 4.8.0 → 5.0.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 +152 -190
- package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.test.js +63 -75
- 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 +77 -100
- 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 +59 -15
- 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 +37 -10
- 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 +7 -24
- package/dist/services/runtime-template.test.js.map +1 -1
- package/package.json +1 -1
- package/templates/.agents/README-ENG.md +49 -7
- package/templates/.agents/README.md +41 -10
- package/templates/.agents/config/agents/discovery.json +1 -1
- package/templates/.agents/dashboard/.zaip-managed.json +125 -73
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-BW7Vy1cp.js → CostCalculationGuidePage-w6pjt4OM.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-BN8KQPrr.js → DashboardHelpDialog-DhlntQcR.js} +117 -17
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Prcs-ydy.js → PricingSettingsDialog-BHoZT2an.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-HqGUImIG.js → ProjectSettingsPage-IcncIZ9Y.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CNpmQMO4.js → TraceabilityWorkspace-C6rORjpr.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-uPnOBShc.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-CF7B1Hjn.js → dashboard-localization-DWXjEIgc.js} +49 -7
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DOhy6D2-.js → icons-vendor-Hc9fp_gs.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{index-DX65Ufuu.js → index-BqeOEZW5.js} +2 -2
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CkPC-yMz.js → react-vendor-C9ta_O10.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-BEdGoIDm.js → traceability-IcefoTOv.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
- package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
- 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/OperationsPage.tsx +10 -36
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +7 -2
- 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/dist/server.cjs +229 -222
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +167 -167
- package/templates/.agents/dashboard/packages/local-runtime/package.json +3 -3
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +229 -222
- 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 +69 -6
- package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.ts +62 -15
- 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 +37 -48
- 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/vitest.config.ts +12 -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/specs/cost-monitor-pagination.spec.ts +13 -6
- package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +1 -1
- package/templates/.agents/design/.zaip-managed.json +3 -3
- package/templates/.agents/design/apps/catalog/package.json +1 -1
- package/templates/.agents/design/packages/local-runtime/package.json +1 -1
- package/templates/.agents/requirements/autonomous-assurance.md +39 -3
- 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 +3 -3
- 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 +11 -2
- 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/cloud-hostinger-autonomy.md +1 -1
- package/templates/.agents/rules/production-readiness.md +24 -0
- package/templates/.agents/rules/run-lifecycle-closure.md +2 -1
- package/templates/.agents/rules/security.md +2 -2
- package/templates/.agents/rules/workflow.md +4 -3
- package/templates/.agents/runtime/VERSION +1 -1
- package/templates/.agents/runtime/bin/zaip.mjs +1185 -610
- 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 +71 -71
- package/templates/.agents/skills/zaip-skill-execution/SKILL.md +4 -2
- package/templates/.agents/skills/zaip-skill-execution/references/controlled-release-recovery.md +44 -0
- package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +1 -1
- package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +7 -1
- package/templates/.agents/skills/zaip-skill-github-deploy/references/release-gate-matrix.md +15 -20
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.mjs +38 -2
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.test.mjs +62 -4
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +8 -6
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/guarded-commits.mjs +5 -3
- package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +7 -0
- 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-prd/SKILL.md +6 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +26 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +46 -11
- package/templates/.agents/skills/zaip-skill-spec/SKILL.md +9 -0
- package/templates/.agents/skills/zaip-skill-testing-strategy/SKILL.md +15 -0
- package/templates/.agents/zaip-aios/VERSION +1 -1
- package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +22 -0
- package/templates/.agents/zaip-aios/guides/14-troubleshooting.md +1 -1
- package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +52 -5
- package/templates/template-manifest.json +210 -126
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Co2Jiuzh.js +0 -68
|
@@ -5,15 +5,18 @@ 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
|
-
"127.0.0.1", "api.github.com", "github.com", "localhost", "objects.githubusercontent.com", "::1",
|
|
19
|
+
"127.0.0.1", "api.github.com", "github.com", "localhost", "objects.githubusercontent.com", "releases.hashicorp.com", "::1",
|
|
17
20
|
]);
|
|
18
21
|
const networkClientPattern = /\b(?:curl|wget|Invoke-WebRequest|Invoke-RestMethod|iwr|irm)\b/i;
|
|
19
22
|
const cloudRunnerScript = ".agents/tools/cloud-runner.mjs";
|
|
@@ -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)) {
|
|
@@ -206,15 +227,20 @@ function validateExplicitFilesystemReferences(command, cwd, reviewedPaths = [])
|
|
|
206
227
|
}
|
|
207
228
|
}
|
|
208
229
|
async function validateGitPublication(command) {
|
|
209
|
-
|
|
230
|
+
const tokens = tokenizeLiteralCommand(command);
|
|
231
|
+
if (tokens !== null) {
|
|
232
|
+
const launcher = tokens[0]?.toLowerCase();
|
|
233
|
+
if (((launcher === "env" || launcher === "command") && /^git(?:\.exe)?$/i.test(tokens[1] ?? "")) ||
|
|
234
|
+
(/^(?:Set-Alias|New-Alias)$/i.test(launcher ?? "") && tokens.slice(1).some(token => /^git(?:\.exe)?$/i.test(token))))
|
|
235
|
+
deny("Indirect Git execution is blocked; use a single literal command.");
|
|
236
|
+
} else if (/(?:^|[;&|\r\n])\s*(?:(?:&\s*)?(?:['"]git(?:\.exe)?['"]|\(\s*['"]git(?:\.exe)?['"]\s*\))|(?:env|command)\s+git(?:\.exe)?\b)/i.test(command) ||
|
|
210
237
|
/&\s*\(\s*Get-Command\s+git(?:\.exe)?\b/i.test(command) ||
|
|
211
238
|
/(?:^|[\s;&|])&\s*\$[A-Za-z_]/i.test(command) || /\b(?:Set-Alias|New-Alias)\b[^\r\n;]*\bgit\b/i.test(command)) {
|
|
212
239
|
deny("Indirect Git execution is blocked; use a single literal command.");
|
|
213
240
|
}
|
|
214
|
-
if (/\(\s*git(?:\.exe)?\b/i.test(command)) {
|
|
241
|
+
if (tokens === null && /\(\s*git(?:\.exe)?\b/i.test(command)) {
|
|
215
242
|
deny("Nested Git execution is blocked; use a single literal command.");
|
|
216
243
|
}
|
|
217
|
-
const tokens = tokenizeLiteralCommand(command);
|
|
218
244
|
if (tokens === null) {
|
|
219
245
|
if (/(?:^|[;&|\r\n(])\s*git(?:\.exe)?\b/i.test(command)) {
|
|
220
246
|
deny("Git commands must be a single literal command; separate commands before requesting execution.");
|
|
@@ -276,6 +302,33 @@ async function validateGitPublication(command) {
|
|
|
276
302
|
}
|
|
277
303
|
}
|
|
278
304
|
|
|
305
|
+
function validateHashicorpRead(command) {
|
|
306
|
+
const tokens = tokenizeLiteralCommand(command);
|
|
307
|
+
if (!tokens || !/^(?:curl(?:\.exe)?|Invoke-WebRequest|Invoke-RestMethod|iwr|irm)$/i.test(tokens[0]))
|
|
308
|
+
deny("HashiCorp releases require a literal supported read command.");
|
|
309
|
+
let urlCount = 0;
|
|
310
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
311
|
+
const token = tokens[i];
|
|
312
|
+
if (/^https:\/\//i.test(token)) {
|
|
313
|
+
const url = new URL(token);
|
|
314
|
+
if (url.hostname !== "releases.hashicorp.com" || url.username || url.password || url.port)
|
|
315
|
+
deny("HashiCorp reads require one exact HTTPS destination.");
|
|
316
|
+
urlCount++; continue;
|
|
317
|
+
}
|
|
318
|
+
if (["-I", "--head", "-s", "-S", "-sS", "--silent", "--show-error", "--fail", "-q", "--disable"].includes(token)) continue;
|
|
319
|
+
const namedMethod = /^(?:--request|--method|-Method)(?:=(.*))?$/i.exec(token);
|
|
320
|
+
const curlMethod = /^-X(.*)$/.exec(token);
|
|
321
|
+
if (namedMethod || curlMethod) {
|
|
322
|
+
const value = namedMethod ? (namedMethod[1] ?? tokens[++i]) : (curlMethod[1] || tokens[++i]);
|
|
323
|
+
if (!/^(?:GET|HEAD)$/i.test(value ?? "")) deny("HashiCorp releases permit only GET or HEAD requests.");
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (/^(?:-Uri|--url)$/i.test(token) && /^https:\/\//i.test(tokens[i + 1] ?? "")) continue;
|
|
327
|
+
deny("Unsupported option in the bounded HashiCorp read command.");
|
|
328
|
+
}
|
|
329
|
+
if (urlCount !== 1) deny("HashiCorp reads require exactly one HTTPS URL.");
|
|
330
|
+
}
|
|
331
|
+
|
|
279
332
|
function validateExplicitNetwork(command) {
|
|
280
333
|
if (/(?:^|[;&|]\s*)\b(?:ssh|scp|sftp|rsync|ftp|telnet|nc|ncat)\b/i.test(command)) {
|
|
281
334
|
deny("Direct remote-shell and file-transfer clients are blocked.");
|
|
@@ -289,174 +342,24 @@ function validateExplicitNetwork(command) {
|
|
|
289
342
|
deny("Network clients require an explicit allowlisted URL.");
|
|
290
343
|
}
|
|
291
344
|
for (const match of urls) {
|
|
292
|
-
let
|
|
345
|
+
let target;
|
|
293
346
|
try {
|
|
294
|
-
|
|
347
|
+
target = new URL(match[0]);
|
|
295
348
|
} catch {
|
|
296
349
|
deny("Malformed network target was rejected.");
|
|
297
350
|
}
|
|
298
|
-
|
|
351
|
+
const host = target.hostname.toLowerCase();
|
|
352
|
+
if (!principalFullNetwork && !allowedExplicitNetworkHosts.has(host)) {
|
|
299
353
|
deny("Explicit network target is outside the ZAIP allowlist.");
|
|
300
354
|
}
|
|
355
|
+
if (host === "releases.hashicorp.com") {
|
|
356
|
+
if (target.protocol !== "https:" || target.username || target.password)
|
|
357
|
+
deny("HashiCorp releases require an exact HTTPS read target.");
|
|
358
|
+
validateHashicorpRead(command);
|
|
359
|
+
}
|
|
301
360
|
}
|
|
302
361
|
}
|
|
303
362
|
|
|
304
|
-
function optionMap(tokens) {
|
|
305
|
-
if (tokens.length % 2 !== 0) return null;
|
|
306
|
-
const values = new Map();
|
|
307
|
-
for (let index = 0; index < tokens.length; index += 2) {
|
|
308
|
-
const key = tokens[index];
|
|
309
|
-
const value = tokens[index + 1];
|
|
310
|
-
if (!key.startsWith("--") || value.startsWith("--") || values.has(key)) return null;
|
|
311
|
-
values.set(key, value);
|
|
312
|
-
}
|
|
313
|
-
return values;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function requireExactOptions(options, allowed, required) {
|
|
317
|
-
if ([...options.keys()].some((key) => !allowed.has(key))) return false;
|
|
318
|
-
return [...required].every((key) => typeof options.get(key) === "string" && options.get(key).length > 0);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function isStringArray(value) {
|
|
322
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string" && item.length > 0);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
async function readCloudContract(options) {
|
|
326
|
-
if (options.get("--contract") !== cloudContractPath) {
|
|
327
|
-
deny("Cloud runner must use the canonical autonomous-cloud contract path.");
|
|
328
|
-
}
|
|
329
|
-
const absolute = resolve(repositoryRoot, cloudContractPath);
|
|
330
|
-
if (!isWithinRepository(absolute)) deny("Cloud runner contract escapes the repository boundary.");
|
|
331
|
-
let contract;
|
|
332
|
-
try {
|
|
333
|
-
contract = JSON.parse(await readFile(absolute, "utf8"));
|
|
334
|
-
} catch {
|
|
335
|
-
deny("Cloud runner contract is absent or malformed.");
|
|
336
|
-
}
|
|
337
|
-
if (
|
|
338
|
-
contract === null ||
|
|
339
|
-
typeof contract !== "object" ||
|
|
340
|
-
contract.version !== 1 ||
|
|
341
|
-
contract.profile !== "autonomous-cloud" ||
|
|
342
|
-
contract.cloudflare === null ||
|
|
343
|
-
typeof contract.cloudflare !== "object" ||
|
|
344
|
-
contract.hostinger === null ||
|
|
345
|
-
typeof contract.hostinger !== "object"
|
|
346
|
-
) {
|
|
347
|
-
deny("Cloud runner contract has no valid autonomous-cloud profile.");
|
|
348
|
-
}
|
|
349
|
-
return contract;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
function cloudflareTargetMatches(contract, options, operation) {
|
|
353
|
-
const cloudflare = contract.cloudflare;
|
|
354
|
-
const record = options.get("--dns-record");
|
|
355
|
-
const managed = isStringArray(cloudflare.dns_records) && cloudflare.dns_records.includes(record);
|
|
356
|
-
const direct = cloudflare.direct_dns_records !== null && typeof cloudflare.direct_dns_records === "object" && Object.hasOwn(cloudflare.direct_dns_records, record);
|
|
357
|
-
return (
|
|
358
|
-
typeof cloudflare.account_id === "string" &&
|
|
359
|
-
typeof cloudflare.zone_id === "string" &&
|
|
360
|
-
isStringArray(cloudflare.allowed_operations) &&
|
|
361
|
-
cloudflare.account_id === options.get("--account-id") &&
|
|
362
|
-
cloudflare.zone_id === options.get("--zone-id") &&
|
|
363
|
-
cloudflare.allowed_operations.includes(operation) &&
|
|
364
|
-
(operation === "cloudflare:dns:read" ? managed || direct : direct && !managed)
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
function hostingerTargetMatches(contract, options, operation) {
|
|
369
|
-
const hostinger = contract.hostinger;
|
|
370
|
-
const role = options.get("--vps-role");
|
|
371
|
-
if (
|
|
372
|
-
typeof hostinger.account_id !== "string" ||
|
|
373
|
-
!isStringArray(hostinger.allowed_operations) ||
|
|
374
|
-
hostinger.account_id !== options.get("--account-id") ||
|
|
375
|
-
!hostinger.allowed_operations.includes(operation) ||
|
|
376
|
-
(role !== "edge" && role !== "application") ||
|
|
377
|
-
hostinger.vps === null ||
|
|
378
|
-
typeof hostinger.vps !== "object"
|
|
379
|
-
) {
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
const target = hostinger.vps[role];
|
|
383
|
-
if (target === null || typeof target !== "object") return false;
|
|
384
|
-
|
|
385
|
-
if (typeof target.id !== "string" || target.id !== options.get("--vps-id")) return false;
|
|
386
|
-
if (operation.startsWith("hostinger:ssh:")) {
|
|
387
|
-
return typeof target.host_key_sha256 === "string" && target.host_key_sha256 === options.get("--host-key-sha256");
|
|
388
|
-
}
|
|
389
|
-
return true;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
function terraformTargetMatches(contract, options, operation) {
|
|
393
|
-
const terraform = contract.terraform;
|
|
394
|
-
if (
|
|
395
|
-
terraform === null ||
|
|
396
|
-
typeof terraform !== "object" ||
|
|
397
|
-
!isStringArray(terraform.allowed_operations) ||
|
|
398
|
-
!terraform.allowed_operations.includes(operation)
|
|
399
|
-
) return false;
|
|
400
|
-
if (operation === "terraform:plan") return terraform.working_directory === options.get("--terraform-directory");
|
|
401
|
-
if (operation === "terraform:apply") {
|
|
402
|
-
const plan = terraform.plan;
|
|
403
|
-
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");
|
|
404
|
-
}
|
|
405
|
-
return terraform.import_targets !== null && typeof terraform.import_targets === "object" && terraform.import_targets[options.get("--resource-address")] === options.get("--resource-id");
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
function validateDirectCloudProviderMutation(command) {
|
|
409
|
-
if (command.includes(cloudRunnerScript)) return;
|
|
410
|
-
if (/\bterraform(?:\.exe)?\s+(?:apply|destroy|import)\b/i.test(command)) deny("Direct Terraform mutation is blocked; use the contract-bound cloud runner.");
|
|
411
|
-
if (/\b(?:wrangler|cloudflare|hostinger)(?:\.exe)?\b/i.test(command)) deny("Direct Cloudflare or Hostinger commands are blocked; use the contract-bound cloud runner.");
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
async function validateCloudRunner(command) {
|
|
415
|
-
const tokens = tokenizeLiteralCommand(command);
|
|
416
|
-
if (tokens === null || tokens.length < 2 || !/^(?:node|node\.exe)$/i.test(tokens[0]) || tokens[1] !== cloudRunnerScript) {
|
|
417
|
-
return false;
|
|
418
|
-
}
|
|
419
|
-
const options = optionMap(tokens.slice(2));
|
|
420
|
-
if (options === null) deny("Cloud runner requires literal, named arguments.");
|
|
421
|
-
const operation = options.get("--operation");
|
|
422
|
-
if (operation === "contract:derive") {
|
|
423
|
-
if (!requireExactOptions(options, new Set(["--operation", "--credentials-source"]), new Set(["--operation", "--credentials-source"]))) {
|
|
424
|
-
deny("Cloud contract derivation has unrecognized arguments.");
|
|
425
|
-
}
|
|
426
|
-
if (options.get("--credentials-source") !== "root") {
|
|
427
|
-
deny("Cloud contract derivation requires the selected root credential source.");
|
|
428
|
-
}
|
|
429
|
-
return true;
|
|
430
|
-
}
|
|
431
|
-
const provider = cloudOperationProviders.get(operation);
|
|
432
|
-
if (provider === undefined) deny("Cloud runner operation is unrecognized or destructive.");
|
|
433
|
-
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"]);
|
|
434
|
-
const baseRequired = provider === "terraform"
|
|
435
|
-
? new Set(["--contract", "--operation", "--credentials-source"])
|
|
436
|
-
: new Set(["--contract", "--operation", "--provider", "--account-id", "--credentials-source"]);
|
|
437
|
-
if (!requireExactOptions(options, common, baseRequired)) {
|
|
438
|
-
deny("Cloud runner invocation has unrecognized arguments.");
|
|
439
|
-
}
|
|
440
|
-
if ((provider !== "terraform" && options.get("--provider") !== provider) || !["root", "agents"].includes(options.get("--credentials-source"))) {
|
|
441
|
-
deny("Cloud runner provider or credential source is outside the approved contract.");
|
|
442
|
-
}
|
|
443
|
-
const contract = await readCloudContract(options);
|
|
444
|
-
const valid = provider === "cloudflare"
|
|
445
|
-
? requireExactOptions(options, common, new Set(["--contract", "--operation", "--provider", "--account-id", "--credentials-source", "--zone-id", "--dns-record"])) && cloudflareTargetMatches(contract, options, operation)
|
|
446
|
-
: provider === "hostinger"
|
|
447
|
-
? requireExactOptions(options, common, new Set(["--contract", "--operation", "--provider", "--account-id", "--credentials-source", "--vps-role"])) && hostingerTargetMatches(contract, options, operation)
|
|
448
|
-
: requireExactOptions(
|
|
449
|
-
options,
|
|
450
|
-
common,
|
|
451
|
-
operation === "terraform:plan"
|
|
452
|
-
? new Set(["--contract", "--operation", "--credentials-source", "--terraform-directory"])
|
|
453
|
-
: operation === "terraform:apply"
|
|
454
|
-
? new Set(["--contract", "--operation", "--credentials-source", "--plan-path", "--plan-sha256", "--policy-result-sha256"])
|
|
455
|
-
: new Set(["--contract", "--operation", "--credentials-source", "--resource-address", "--resource-id"]),
|
|
456
|
-
) && terraformTargetMatches(contract, options, operation);
|
|
457
|
-
if (!valid) deny("Cloud runner target, operation, or host identity is outside the approved contract.");
|
|
458
|
-
return true;
|
|
459
|
-
}
|
|
460
363
|
|
|
461
364
|
${CODEX_STALE_GIT_LOCK_HELPER}
|
|
462
365
|
|
|
@@ -533,6 +436,10 @@ async function validateShellDeletion(command, cwd) {
|
|
|
533
436
|
}
|
|
534
437
|
|
|
535
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
|
+
}
|
|
536
443
|
const paths = patchPaths(command);
|
|
537
444
|
const lifecycleRecordTouched = paths.some((path) =>
|
|
538
445
|
/^\.agents[\\/]records[\\/](?:prds|specs|tasks)[\\/]/i.test(path));
|
|
@@ -541,8 +448,10 @@ async function validatePatch(command) {
|
|
|
541
448
|
}
|
|
542
449
|
for (const path of paths) {
|
|
543
450
|
const absolute = resolve(repositoryRoot, path);
|
|
544
|
-
if (
|
|
545
|
-
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.");
|
|
546
455
|
if (repositoryPath(absolute) === "") deny("Patching the repository root is blocked.");
|
|
547
456
|
if (protectedTaskWorkspacePath(repositoryPath(absolute))) deny("Task workspace claims are managed only by the canonical CLI.");
|
|
548
457
|
if (isGitPath(absolute)) {
|
|
@@ -564,13 +473,25 @@ async function requiresOrchestratorRun(event, command) {
|
|
|
564
473
|
return paths.length === 0 || paths.some((path) => !lifecycleExemptPathPattern.test(path));
|
|
565
474
|
}
|
|
566
475
|
if (event.tool_name === "Edit" || event.tool_name === "Write") {
|
|
567
|
-
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.");
|
|
568
478
|
const path = repositoryPath(resolve(event.cwd ?? repositoryRoot, command));
|
|
569
479
|
if (protectedTaskWorkspacePath(path)) deny("Task workspace claims are managed only by the canonical CLI.");
|
|
570
480
|
return !lifecycleExemptPathPattern.test(path);
|
|
571
481
|
}
|
|
572
482
|
// Only single literal writes claim exemptions; compound commands remain gated.
|
|
573
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;
|
|
574
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] ?? "") &&
|
|
575
496
|
tokens.some(token => token.toLowerCase().includes("task-workspaces") &&
|
|
576
497
|
protectedTaskWorkspacePath(repositoryPath(resolve(event.cwd ?? repositoryRoot, token)))))
|
|
@@ -585,40 +506,81 @@ async function requiresOrchestratorRun(event, command) {
|
|
|
585
506
|
}
|
|
586
507
|
if (valid && options.has("-literalpath") && options.has("-value")) {
|
|
587
508
|
const path = repositoryPath(resolve(event.cwd ?? repositoryRoot, options.get("-literalpath")));
|
|
588
|
-
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.");
|
|
589
511
|
if (protectedTaskWorkspacePath(path)) deny("Task workspace claims are managed only by the canonical CLI.");
|
|
590
512
|
if (lifecycleExemptPathPattern.test(path)) return false;
|
|
591
513
|
}
|
|
592
514
|
}
|
|
515
|
+
// The external Node is only a literal canonical validation command when the
|
|
516
|
+
// native authority has verified this workspace's exact opt-in executable.
|
|
517
|
+
if (tokens && isAbsolute(tokens[0]) &&
|
|
518
|
+
/^\.agents[\\/]runtime[\\/]bin[\\/]zaip\.mjs$/.test(tokens[1] ?? "") &&
|
|
519
|
+
tokens[2] === "validation" && ["execute", "batch"].includes(tokens[3]) &&
|
|
520
|
+
!Object.values(shellSyntax(command)).some(Boolean)) {
|
|
521
|
+
let optIn;
|
|
522
|
+
try { optIn = JSON.parse(await readFile(resolve(repositoryRoot, ".local/validation-runtime.json"), "utf8")); }
|
|
523
|
+
catch { optIn = null; }
|
|
524
|
+
const reviewed = await (nativeAuthority?.reviewedValidationRuntimePaths?.(command, tokenizeLiteralCommand, repositoryRoot) ?? []);
|
|
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;
|
|
528
|
+
}
|
|
593
529
|
return requiresShellMutationRun(command, tokens, event.cwd ?? repositoryRoot, repositoryRoot);
|
|
594
530
|
}
|
|
595
531
|
|
|
596
|
-
|
|
597
|
-
const info = await lstat(path);
|
|
598
|
-
if (!info.isFile() || info.isSymbolicLink() || info.size > maximumBytes) throw new Error("Unsafe Run lookup file");
|
|
599
|
-
const physical = await realpath(path);
|
|
600
|
-
const identity = value => process.platform === "win32" ? value.toLowerCase() : value;
|
|
601
|
-
if (!inside(physical, authorityRoot) || identity(physical) !== identity(resolve(path))) throw new Error("Run lookup must not follow links");
|
|
602
|
-
return readFile(path, "utf8");
|
|
603
|
-
}
|
|
532
|
+
const boundedCanonicalText = (path, maximumBytes, authorityRoot = repositoryRoot) => readBoundedCanonicalText(path, maximumBytes, authorityRoot);
|
|
604
533
|
|
|
605
534
|
let raw = "";
|
|
606
535
|
for await (const chunk of process.stdin) raw += chunk;
|
|
607
536
|
|
|
608
537
|
let event;
|
|
609
|
-
|
|
610
|
-
event = JSON.parse(raw);
|
|
611
|
-
} catch {
|
|
612
|
-
deny("Malformed hook input was rejected.");
|
|
613
|
-
}
|
|
538
|
+
event = parseHookEvent(raw, deny);
|
|
614
539
|
|
|
615
540
|
const toolInput = event?.tool_input;
|
|
616
|
-
|
|
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 }; }
|
|
617
574
|
catch (error) { deny(error.message); }
|
|
618
|
-
if (
|
|
575
|
+
if (credentialReferencePattern.test(event.cwd)) deny("Direct credential and secret access is blocked.");
|
|
576
|
+
if (!principalFullAccess && !isWithinRepository(event.cwd)) {
|
|
619
577
|
deny("Tool execution outside the ZAIP-AIOS repository is blocked.");
|
|
620
578
|
}
|
|
621
579
|
|
|
580
|
+
const { validateCloudRunner, validateDirectCloudProviderMutation } = createCloudValidators({
|
|
581
|
+
tokenizeLiteralCommand, deny, repositoryRoot, isWithinRepository, cloudRunnerScript, cloudContractPath, cloudOperationProviders,
|
|
582
|
+
});
|
|
583
|
+
|
|
622
584
|
const commandFromCmd = toolInput?.cmd;
|
|
623
585
|
const commandFromCommand = toolInput?.command;
|
|
624
586
|
if (typeof commandFromCmd === "string" && typeof commandFromCommand === "string" && commandFromCmd !== commandFromCommand) {
|
|
@@ -627,7 +589,7 @@ if (typeof commandFromCmd === "string" && typeof commandFromCommand === "string"
|
|
|
627
589
|
const command = typeof commandFromCmd === "string" ? commandFromCmd : commandFromCommand;
|
|
628
590
|
if (typeof command !== "string") deny("A matched tool call without a command was rejected.");
|
|
629
591
|
|
|
630
|
-
if (await requiresOrchestratorRun(event, command)
|
|
592
|
+
if (await requiresOrchestratorRun(event, command)) {
|
|
631
593
|
const directRun = await nativeAuthority?.hasCurrentTaskOrchestratorRun(event, installedRoot, boundedCanonicalText);
|
|
632
594
|
const primaryRead = (path, maximumBytes) => boundedCanonicalText(path, maximumBytes, installedRoot);
|
|
633
595
|
const delegated = directRun ? false : await nativeAuthority?.hasNativeDelegatedPatch(event, repositoryRoot, primaryRead, installedRoot);
|
|
@@ -644,9 +606,9 @@ if (event.tool_name === "apply_patch") await validatePatch(command);
|
|
|
644
606
|
else {
|
|
645
607
|
const reviewedPaths = [
|
|
646
608
|
...await reviewedReleasePaths(command, event.cwd, repositoryRoot, credentialReferencePattern, deny),
|
|
647
|
-
...await reviewedContextReadPaths(command, credentialReferencePattern), ...await (nativeAuthority?.reviewedValidationRuntimePaths?.(command, tokenizeLiteralCommand) ?? []),
|
|
609
|
+
...await reviewedContextReadPaths(command, credentialReferencePattern, installedRoot), ...await (nativeAuthority?.reviewedValidationRuntimePaths?.(command, tokenizeLiteralCommand, repositoryRoot) ?? []),
|
|
648
610
|
];
|
|
649
|
-
validateExplicitFilesystemReferences(command, event.cwd, reviewedPaths);
|
|
611
|
+
await validateExplicitFilesystemReferences(command, event.cwd, reviewedPaths);
|
|
650
612
|
validateInlineExecution(command, deny);
|
|
651
613
|
if (!isLiteralReadSequence(command)) {
|
|
652
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"}
|