diff --git a/README.md b/README.md index 709a3e3..90bfa7a 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,14 @@ it does not replace Codex. ### 1. Install, then restart Codex ```bash -codex plugin marketplace add VAMFI/codsemble --ref fc247152961c25e79b525a969806991f499a2bac +codex plugin marketplace add VAMFI/codsemble --ref 0aa84be36209e454fadfb34b96c8c3d5b3a16caf codex plugin add codsemble@codsemble ``` Open a fresh Codex session so its four Codesemble skills are discovered. The -pinned commit is the merged, validated v0.2 candidate. +pinned commit is the locally validated v0.2 privacy-hardening candidate. It is +not merged, tagged, or released yet; use it only after that commit is available +from the configured Git source. ### 2. Audit, choose, and approve @@ -161,9 +163,10 @@ publish anything. ## Project status -Codesemble v0.2.0 is merged and validated on `main`. It is installable from the -pinned Git source above, but has not been tagged or released; v0.1.0 remains the -latest release. +The v0.2 code line is present on `main`, but the privacy-hardening candidate +documented here is one local commit ahead of the recorded `origin/main`. It is +validated locally and has not been pushed, merged, tagged, or released; v0.1.0 +remains the latest release. Validation claims remain tied to the exact tested payload and environment. [See the evidence →](docs/VALIDATION.md) diff --git a/artifacts/CHECKSUMS.sha256 b/artifacts/CHECKSUMS.sha256 index ff93676..5ba0c2f 100644 --- a/artifacts/CHECKSUMS.sha256 +++ b/artifacts/CHECKSUMS.sha256 @@ -12,9 +12,9 @@ abe6e3180a147cf12578d7ded299447f251cf4da27bd05e2eaaa394f7447e635 .github/script e2408324959756ec500eab662eba6626f97b485532bd27c870f2776ef35b0fba .github/workflows/dependency-review.yml 9a35856fb32b4d24ff3c3a6f60bc6a61d18d18595d2ca11f50c42baefaf519b9 .gitignore 380658625376043b8a609351d5b0687315b71267f66b4a41c02d47fa8944a746 AGENTS.md -d93fd8a98237933dbac1a93dcca87ce3733cc7d2ffaadd682b1294656942097b artifacts/codsemble-0.2.0-plugin.tar +a3635eb7878a81169663e0dcf4d79e4a18a196e2e692e310ae82e3b2c8928d03 artifacts/codsemble-0.2.0-plugin.tar 81807f0376ad3859d117f1f78dc22e9b7c879741d12adb8c4f04eed7f73c3222 artifacts/codsemble-0.2.0-rc.sbom.cdx.json -734a74c5362cf472c155f69e29fba6b57a1b921b179f4f5777360351735bf272 artifacts/runtime-evidence.json +30002ffe47fd2b1f850229d4f95acfccebef9d80823d3f2be99e5d7aac1b8ac1 artifacts/runtime-evidence.json 4fc4a92325b0ef2ca5b58c3aa968d0ab462698f001249d4d3974cb838576737a assets/brand/codsemble-github-hero.png e9fa0ce1862c3da704746c8ccd0d6df504c8eae65a8a61992507b57f2dda2d5e assets/brand/codsemble-github-social-preview.png ca34cfa4c7ad03edfc6b1dcf764fe44f6cb3465480f7177392ecf2c22e3c979c assets/social/concepts/codsemble-concept-native-compiler.png @@ -31,7 +31,7 @@ f41132adc833328e17b1e304ef41c4a3f72401b674d997f56269b3481e7b45d6 docs/CONFIG_SA 015308700a7ceb71a073b7e7a619d44ec3367db0a33a49b73aab4f3346c209ab docs/DEFINITION_OF_DONE.md 12f477b541235324919616d9389c72326b418a9354c9ea0c075a808fc9eca8bd docs/index.html 7f585f942f10a18f836b53d822028f39a2d5b6bf91f16b1218ab73aeee140047 docs/MIGRATION_V0_2.md -7b1b0fae02c9f36405ab7ca0ac97b5bf69e71b23a23e9e7ab8af484170fdd8ee docs/PRIVACY.md +c03332b728f036084c557f0839483cfa8e21e34a3c331c2a15308841d042c8fc docs/PRIVACY.md 5a54e204e4c0e9357442784225835f2bf0c865fb174351d175b4167e990addf9 docs/PROJECT_CAPABILITY_COMPILER.md 5bbc7300b711e5e25085d3282e2e234f264baf96d189305e33e5fd9e7832d3de docs/ROLE_CATALOG.md c027a40af66414303146cc7e2648af8f310e54329ba31c33e3dcd73c7b707a2a docs/script.js @@ -39,7 +39,7 @@ c027a40af66414303146cc7e2648af8f310e54329ba31c33e3dcd73c7b707a2a docs/script.js a992f4b3cb388bac1a5d43b9a31a13be80505778894211aa46761e64e4fe56af docs/THREAT_MODEL.md e03c31ce5087e8a203f2500791228e3c5fa9dfbb8bebf2f7352e2c31e14ee93e docs/USAGE.md 05575afa5edbb76589ee5ee00fcc3b1a07cb62a02c2b42fa204115c4e4742540 docs/USEFULNESS_COMPARISON.md -add9f838a098489da1f97d44ac270be5c27b607134494656571671f6b2db5907 docs/VALIDATION.md +79eb8878fe1fb3668e35bf3f8bb1d82f4608a2b5c3765bc65f4c955a823f2e48 docs/VALIDATION.md cecb778f5577db034b190a7a85ece2d89fa6981f6eb80448d95f8e728a55ee8e docs/VOICE_APPROVAL.md 41e996c5a736d7d7011bb1eba7cdfaff1ec0379c98bbc9351333824962e63fcd examples/intake.preview.json 06b6a2f5136c646ae2da2cd1125ad656dc176e36177ff032f95778b5d3558889 examples/intake.runtime.json @@ -51,7 +51,7 @@ b89d0cb888d7afade19b1694a4a2c2ba44a45038cf3a02565631b54463c1b556 NOTICE fdf56e8a9fdcda071e8d6ca5e00ba3ad207d369a62d27ed7af39e68fc7f554ad plugins/codsemble/.codex-plugin/plugin.json 3d5a796d8ee135c6a00d7d98b94582c560b2119f81be5b8da30434b5a3d0df62 plugins/codsemble/catalog/generate.mjs edd6d03447adb073a2aebd71e0e0b94b5c3a6fa31f4492cd6035623076b672b6 plugins/codsemble/catalog/roles.json -754fde66e2ae77a82ed2edaafbdd895ad6d1335c28a840221c4225d4afbfe854 plugins/codsemble/scripts/codsemble.mjs +2603e046f30f6da23a8dc9dec937e7ac4b437786293b841ad87d4b4f78ba8c98 plugins/codsemble/scripts/codsemble.mjs 887c3b5fa744a0214988b5d8422c22f385a626bc19ba4582913b2d25ff621fb3 plugins/codsemble/skills/initialize-team/agents/openai.yaml 56bd21c0c3a0f4fb82e6b2d3362df7f360ee6860ea6919e54d6e562d99da8a29 plugins/codsemble/skills/initialize-team/SKILL.md 1a476efd980ab66f88580650102255b45b26b8a7c7ea74471a8c985915fd8f8d plugins/codsemble/skills/rollback-team/agents/openai.yaml @@ -61,7 +61,7 @@ ec89a95f29bb67625a44663c08b0939cf522158cd094e31015b83c1a20c2062b plugins/codsem a6689d5a15007ba0942ca9ff9538be7a3fc8e1c4b0993191b928063161f57098 plugins/codsemble/skills/update-team/agents/openai.yaml d9297755666b1e88886564fc71bc060872d813c9fe043dd94954a210612491ba plugins/codsemble/skills/update-team/SKILL.md 152eb46211db24f9e4bdb3dd38a11ab0aa7ea6a19bc8a95c0b5d9521fc8ed289 PROJECT_GOAL.md -76371ac04ed555fd465ad55dc82738ee6a9290a35e3a47917b45048df12c9085 README.md +609177ecbbb5f21e05e62e9dce4a18ca23a95c869f2c899b3f96e224d7dc778a README.md 0504448900848d23d063b412d816ad2f5c5e786b4e6e0cb8754803f8f588cc2d ROADMAP.md d2d0dd1a73d5d788475bc040d23b617f7d0ee324d8b40bd081a68b27eada18d2 scripts/build.mjs c6581520fed9f775f87ef1e054c46e786129f03d79b7ad31a441c59e4bdd343a scripts/checksums.mjs @@ -70,7 +70,7 @@ d6da5bff64766adee163b8b9b7d427d0c3ef6c93ee976d23e051015740616167 scripts/plugin a034f12cabe190a207a1d360dfe0563047a3dd3adefdfbb35e4886dfcf0ff8c9 scripts/sbom.mjs f210300ed0af67e0b8e76f89f46ed8993aeb597381c3b6ec67d62c254679d3d8 scripts/validate-catalog.mjs ce3cc1e66b2c89a5103aa3360ef04a60b0c983f84b7221ef3174cb8778f9795d SECURITY.md -42098042ff15de52ece62e2da96de213f5c093650719daddf25206714991f7e1 src/audit.ts +c2e1292b349a26f6636c2563b05f4e10af5f0bb47e7609f427f4e3c061299475 src/audit.ts 4330112db7e3a02439292a6fae4cf94d95d52369fe54e07107391274135034c9 src/capabilities.ts d80c81d121e10e1e5e9834274a504fbaf598520bc6dd274856ade7fbb85e4fc1 src/capability-compiler.ts 9961f538a6de4dfd06a6575bf1c626924bce62f86ac0573f4eb288aa8e8652b1 src/catalog.ts @@ -87,7 +87,7 @@ b0c2ff5339194d873f7c5d01450460a7606cfff23161cadc38b53248b56e2da7 src/transactio ed805ca057f1d4ad80fa9c43e68f2df7c41579f0795c01699f46e6987d8e67d6 src/types.ts 1cf68b2b4cec5b49cfa9e7ab97aeb0ad5cb1c4484793df96579475e4967b0bcf src/util.ts 718e08fc0bf75b7d81c7162000bebaec428f1832519a3aeb10e891c76215fd8e SUPPORT.md -636189aa361e44eda68a69951e3c50f9dd2f982dcfd2f77db81eb8470b138bc5 tests/audit.test.ts +3d769eaf7a75eeb861088daa7bd1cc022f1ee4b7b523029a8bb102b0b30281ef tests/audit.test.ts 34fdf298b89d17f94be38f6a43f00c7d92d1e0a7f4617f95134d7c2834b0ef02 tests/capabilities.test.ts e2fcead083b9568ab6919ef6ed97f039455d1869794e7c6aac5bb4a67f04acb4 tests/capability-compiler.test.ts 536db87b22f1ecf4977871da642eb65629b2b426891b878f96105f0aeef9cac1 tests/catalog.test.ts diff --git a/artifacts/codsemble-0.2.0-plugin.tar b/artifacts/codsemble-0.2.0-plugin.tar index cb3ad7d..51ebf37 100644 Binary files a/artifacts/codsemble-0.2.0-plugin.tar and b/artifacts/codsemble-0.2.0-plugin.tar differ diff --git a/artifacts/runtime-evidence.json b/artifacts/runtime-evidence.json index abc5798..395ee3d 100644 --- a/artifacts/runtime-evidence.json +++ b/artifacts/runtime-evidence.json @@ -1,196 +1,88 @@ { - "schemaVersion": 2, - "capturedAt": "2026-08-01T19:08:52Z", + "schemaVersion": 3, + "capturedAt": "2026-08-07T21:02:17Z", "source": { - "validatedCommit": "506d843a627c4fd225109eb6fd3fe082d1f2bd26", - "pluginArchiveSha256": "d93fd8a98237933dbac1a93dcca87ce3733cc7d2ffaadd682b1294656942097b", - "logicalPluginPayloadSha256": "95c65a6f991fc1300d6af0551bf1934d6a7beb153008220aa9cd5f7f69e3a005", + "validatedCommit": "0aa84be36209e454fadfb34b96c8c3d5b3a16caf", + "validatedCommitRelationship": "privacy-hardening code and generated plugin artifacts; evidence/documentation changes are a subsequent local commit", + "pluginArchiveSha256": "a3635eb7878a81169663e0dcf4d79e4a18a196e2e692e310ae82e3b2c8928d03", + "logicalPluginPayloadSha256": "042bf4e58914f46db6cb281157ce80848faed06692efcee595fdbebcc86b6836", "logicalPluginPayloadDigestAlgorithm": "sha256(path-nul-length-nul-content-nul)", - "pluginFileCount": 12 + "pluginFileCount": 12, + "sbomSha256": "81807f0376ad3859d117f1f78dc22e9b7c879741d12adb8c4f04eed7f73c3222", + "checksumEntryCount": 127 }, "boundary": { - "codexVersion": "0.145.0", - "nodeVersion": "26.5.0", - "additionalValidationNodeVersion": "24.13.1", - "operatingSystem": "Darwin 25.5.0 arm64", + "codexVersion": "0.147.0-alpha.6.5", + "nodeVersion": "24.14.0", + "operatingSystem": "macOS 26.6.1 build 25G76 arm64", "pluginVersion": "0.2.0", "pluginId": "codsemble@codsemble", "marketplace": "codsemble", - "configAdapter": "agents-v1", - "isolatedCodexHome": true, - "disposableTrustedProject": true, - "globalCodexConfigurationChanged": false + "marketplaceSource": "local privacy-hardening worktree", + "configAdapter": null, + "isolatedCodexHome": false, + "disposableTrustedProject": false, + "project": "portable-agent-memory", + "globalCodexConfigurationChanged": false, + "networkRequiredForRuntimeChecks": false }, "pluginDiscovery": { - "installedFromExactArchive": true, "installed": true, "enabled": true, - "freshSkillDiscovery": "pass", - "configLoad": "pass" + "installedFromExactLocalCandidate": true, + "freshSkillAndAgentConfigLoad": "pass" }, - "previewOnlyPlan": { - "planId": "9e1ce741e87f831de31788b5", - "applyCapable": false, - "confirmationChallengeIssued": false, - "writesObserved": 0, - "result": "pass" + "confirmedTeamUpdate": { + "planId": "ce81caf1b50ff9e44f535a1f", + "transactionId": "d146674e-5122-4c71-8769-ee132174cf06", + "confirmationMethod": "exact plan confirmation identifier supplied by the user", + "configMode": "unchanged", + "configWriteApplied": false, + "installedRoleCount": 3, + "maxConcurrentWorkers": 2, + "doctorAfterApply": "pass with intentional no-project-config warning" }, - "initialApply": { - "planId": "21e7c47ef95ff586ef8f4a40", - "confirmationId": "dd64148528bc94bd5784c376d98bde68", - "confirmationMethod": "exact six-word voice-friendly challenge", - "confirmationChallengeIncluded": false, - "transactionId": "b1984813-86eb-45d1-be19-e91c39b75ba1", - "configMode": "apply-project", - "projectCurrentSpawnedWorkerCeiling": null, - "requestedSpawnedWorkerCeiling": 2, - "configWriteApplied": true, - "installedRoleCount": 8, - "generatedRoleCount": 6, - "explicitRoleCount": 2, - "explicitRoles": [ - { - "id": "runtime-master-orchestrator", - "model": "gpt-5.6-sol", - "reasoningEffort": "ultra" - }, - { - "id": "runtime-daily-integrator", - "model": "gpt-5.6-luna", - "reasoningEffort": "max" - } - ], - "doctorAfterApply": "pass" + "freshTypedDelegation": { + "parentThreadId": "019fde08-1a4b-7760-ab6b-9e2eb8dced65", + "agentRole": "documentation_present_documentation_specialist_8076292f", + "forkTurns": "none", + "assignment": "read the bounded evaluation document and identify its title and generalization caveat", + "result": "pass", + "filesChanged": 0, + "networkUsed": false }, - "nativeOrchestrationRun": { - "parentThreadId": "019fbe45-0f97-7202-83fa-1bebabe8687f", - "orchestrator": { - "threadId": "019fbe45-2fe0-7ed1-a00f-286f4dda290c", - "agentRole": "runtime-master-orchestrator", - "generatedDeveloperInstructionsObserved": true - }, - "specialists": [ - { - "threadId": "019fbe45-55c3-7543-a489-7a3efb0d0aba", - "agentRole": "javascript-implementation-specialist-2a9ed216", - "source": "generated", - "discoveredInFreshPostApplySession": true, - "assignment": "source inspection", - "result": "SOURCE-OK" - }, - { - "threadId": "019fbe45-877a-7640-8582-5d8f56350cf7", - "agentRole": "tests-present-verification-specialist-c2eadc5d", - "source": "generated", - "discoveredInFreshPostApplySession": true, - "assignment": "focused test execution", - "result": "TEST-OK; one focused test passed" - } - ], - "rootIntegratedResult": "ROOT-OK ORCH-OK SOURCE-OK TEST-OK", - "rootRetainedFinalAuthority": true + "freshTrivialNoSpawn": { + "parentThreadId": "019fde08-75b4-78d1-b40b-c30438e480ce", + "assignment": "read the project version from pyproject.toml", + "result": "0.1.0", + "spawnedChildren": 0, + "filesChanged": 0, + "networkUsed": false }, - "capacityRun": { - "parentThreadId": "019fbe46-5d6e-7441-92e7-c874ded6285d", - "configuredSpawnedWorkerCeiling": 2, - "successfulChildren": [ - "019fbe46-6d06-71e3-a143-b14128627c71", - "019fbe46-7db9-7801-a93c-16d4b759f284" - ], - "thirdSpawn": "rejected: agent thread limit reached", - "unboundedRetryObserved": false, - "result": "CAPACITY-OK TWO-ACTIVE THIRD-REJECTED NO-RETRY" - }, - "trivialRun": { - "parentThreadId": "019fbe47-0cc9-76e2-a2f7-b995b51bd528", - "result": "TRIVIAL-OK 4", - "spawnedChildren": 0 - }, - "meaningfulUpdate": { - "planId": "dd6f15213429d39c796cdb3a", - "transactionId": "f670f4af-4408-44ef-b4b9-ed7bf0c65b82", - "previousSpawnedWorkerCeiling": 2, - "requestedSpawnedWorkerCeiling": 3, - "configWriteApplied": true, - "doctorAfterApply": "pass" - }, - "convergenceUpdate": { - "planId": "44a1438cb506b2d6dc9bbc30", - "transactionId": "739d998d-8ca7-4886-8179-0420beadd1ae", - "changedFiles": [ - ".codex/codsemble/manifest.json" - ], - "doctorAfterApply": "pass" - }, - "idempotentUpdate": { - "planId": "44a1438cb506b2d6dc9bbc30", - "stablePlanIdAcrossConvergence": true, - "verifiedManagedFiles": 11, - "allManagedActions": "verify", - "noChanges": true, - "transaction": null, - "reloadRequired": false, - "receiptCountBefore": 3, - "receiptCountAfter": 3, - "doctorAfterNoOp": "pass" - }, - "rollback": { - "skillVersion": "0.2.0", - "previewedBeforeEachApply": true, - "forceUsed": false, - "transactionsRevertedInReverseOrder": [ - "739d998d-8ca7-4886-8179-0420beadd1ae", - "f670f4af-4408-44ef-b4b9-ed7bf0c65b82", - "b1984813-86eb-45d1-be19-e91c39b75ba1" - ], - "doctorAfterIntermediateRollbacks": "pass", - "doctorAfterFinalRollback": "warn-as-uninitialized", - "generatedAgentFilesRemaining": 0, - "projectConfigPresent": false, - "manifestPresent": false, - "agentsInstructionsPresent": false, - "transactionReceiptCount": 3, - "rollbackMarkerCount": 3, - "transactionRecoveryHistoryRetained": true, - "trackedProjectDiffAfterRollback": "clean", - "onlyUntrackedPathAfterRollback": ".codex/ transaction recovery history", - "preservedInputSha256": { - "README.md": "b037d09a1b7002409f8bbffbaa863ee5d9d4a1c5cbca22eba4b454247806846a", - "package.json": "d994c9ac7dadcdc30a25d12de698fb39bd9e2673913aaa747c38c8ed2dc8febe", - "Dockerfile": "b849d6cf7136339d0ecfb75b553230c5cde5ac86390c5a56facd7674e0e4f932", - "src/service.js": "5258fdccf53070d8df1eda40b801edadd3a84df72ac715c6c3f3140cffdd90a6", - "test/service.test.js": "aa2bf92850954dcefaf398ba50a5cca14bfe418c690585ae797ed76f4043a6d1" - } - }, - "validation": { - "tests": "140/140 pass on Node 26.5.0 and Node 24.13.1", - "build": "pass", + "localValidation": { + "tests": "141/141 pass across 20 files", "typecheck": "pass", - "catalogValidation": "pass", - "checksumEntries": 120, - "sbomComponents": 128, - "archiveDeterminism": "pass", - "publicCiAtValidatedCommit": { - "ubuntuNode22": "pass", - "ubuntuNode24": "pass", - "macosNode22": "pass", - "macosNode24": "pass", - "windowsNode22": "pass", - "windowsNode24": "pass", - "dependencyReview": "pass", - "codeql": "pass" - } + "build": "pass", + "catalogValidation": "pass; 111 compatibility primitives", + "repositoryValidation": "pass; 4 skills and 15 documents", + "officialPluginValidation": "pass", + "officialSkillValidation": "pass for all 4 plugin-owned skills", + "archiveDeterminism": "pass; 12 files", + "sbomDeterminism": "pass; CycloneDX 1.5 with 128 components", + "sourceChecksums": "pass; 127 entries", + "bundledCliSmoke": "pass", + "dependencyAudit": "pass; zero reported vulnerabilities" }, "evidencePolicy": { "rawSessionFilesIncluded": false, "credentialsIncluded": false, "absolutePersonalPathsIncluded": false, - "confirmationChallengeWordsIncluded": false + "confirmationSecretIncluded": false }, - "knownGaps": [ - "The exact v0.2 runtime boundary was exercised on one Darwin arm64 host with Codex 0.145.0; cross-platform code validation is represented by public CI.", - "A physical Android microphone and speech-recognition loop was not exercised; voice approval was validated through the exact CLI challenge semantics.", - "The raw isolated session directory was intentionally excluded because it referenced local authentication state.", - "Repeated icon warnings came from the separately cached Teams plugin; Codesemble skill metadata does not declare those icons." + "pendingExternalOrSeparateBoundaries": [ + "Public pull-request CI and CodeQL have not run at the final evidence/documentation commit.", + "An isolated Codex-home apply and reverse rollback were not repeated for this exact privacy-hardening payload.", + "Physical Android microphone and speech-recognition behavior remains untested.", + "Push, merge, tag, signed provenance, GitHub release, marketplace publication, and directory submission remain unperformed." ] } diff --git a/docs/PRIVACY.md b/docs/PRIVACY.md index 4151585..268ce99 100644 --- a/docs/PRIVACY.md +++ b/docs/PRIVACY.md @@ -24,7 +24,13 @@ plugin's boundary. The auditor excludes: -- `.env` files and common credential, key, certificate, auth, and token paths; +- `.env` files and common credential, key, certificate, auth, OAuth, API-key, + service-account, access-token, and refresh-token stores. Auth/token matching + is limited to boundary-delimited configuration/data filenames such as JSON, + YAML, TOML, plist, properties, and XML plus named CLI/cloud auth stores; + ordinary source and documentation such as `src/auth.ts` and `docs/AUTH.md` + remain eligible. Ambiguous config names such as `design-tokens.json` fail + closed and are excluded; - files ignored by the repository; - dependency caches, build output, generated artifacts, and large binaries; - symlinks and paths outside the selected workspace; diff --git a/docs/VALIDATION.md b/docs/VALIDATION.md index 4f51841..053f5b4 100644 --- a/docs/VALIDATION.md +++ b/docs/VALIDATION.md @@ -26,7 +26,7 @@ downloaded by the project. The current local candidate passes: - strict TypeScript checking; -- 140 automated tests across 20 files, including audit, capability compilation, +- 141 automated tests across 20 files, including audit, capability compilation, representative fixtures, semantic golden/property behavior, generated-role admission, evidence freshness, voice confirmation, CLI integration, manifest shared strict lifecycle lineage, trusted-Git PATH refusal, no-clobber @@ -89,22 +89,24 @@ filesystem simulation boundary, not native Codex discovery on Windows. ## Native Codex runtime -`artifacts/runtime-evidence.json` records the v0.2 Project Capability Compiler -run against the exact archived plugin in an isolated Codex home and disposable -trusted project. It records: +`artifacts/runtime-evidence.json` records the current privacy-hardening +candidate's exact source and payload hashes, local validation, and native Codex +runtime checks. The current record uses the active local Codex home and the +tracked Portable Agent Memory project; it does not claim an isolated-home or +disposable-project boundary. It records: - source commit, archive SHA-256, logical payload digest, Codex/Node versions, OS, architecture, and adapter; -- plugin discovery and one non-catalog project-generated role; -- preview no-write, exact confirmed apply, and fresh-session role discovery; -- one attributable separable delegation plus primary-thread integration; -- bounded capacity rejection without a retry storm and a trivial no-spawn turn; -- meaningful update, no-op convergence, and reverse rollback preserving user bytes. - -The record excludes authentication material, absolute personal paths, raw -sessions, and confirmation challenge words. The evidence-only commit that adds -the record does not change the archived plugin payload; final-head CI remains a -separate required check. +- installed plugin discovery from the exact local candidate; +- an exact-confirmed project-team update and doctor result; +- fresh-session discovery and one typed bounded delegation; +- a separate trivial no-spawn turn; and +- the local source, archive, SBOM, checksum, and test boundary. + +The record excludes authentication material, raw sessions, and confirmation +challenge words. It uses repository-relative project identifiers rather than +personal paths. Exact-head public CI, isolated-home apply/rollback, and release +publication remain separate required checks. ## Voice evidence diff --git a/plugins/codsemble/scripts/codsemble.mjs b/plugins/codsemble/scripts/codsemble.mjs index 5219a80..6915981 100755 --- a/plugins/codsemble/scripts/codsemble.mjs +++ b/plugins/codsemble/scripts/codsemble.mjs @@ -1203,12 +1203,28 @@ function normalizeRelativePath(value) { return normalized; } function isSecretLike(relativePath) { - const segments = relativePath.toLowerCase().split("/"); + const normalized = relativePath.toLowerCase(); + const knownAuthStores = [ + ".docker/config.json", + ".config/gh/hosts.yml", + ".config/glab-cli/config.yml", + ".azure/accesstokens.json" + ]; + if (knownAuthStores.some( + (storePath) => normalized === storePath || normalized.endsWith(`/${storePath}`) + )) { + return true; + } + if (path2.posix.basename(normalized) === "serviceaccountkey.json") { + return true; + } + const segments = normalized.split("/"); return segments.some((segment) => { if (segment === ".env" || segment.startsWith(".env.") || segment === ".npmrc" || segment === ".pypirc" || segment === ".netrc" || segment === "credentials" || segment === "credentials.json" || segment === "secrets.json" || segment === "secrets.yaml" || segment === "secrets.yml" || segment === "id_rsa" || segment === "id_ed25519") { return true; } - return /(?:^|[._-])(secret|secrets|credential|credentials)(?:[._-]|$)/.test(segment) || /\.(?:key|pem|p12|pfx|jks|keystore)$/.test(segment); + const secretBearingConfig = /\.(?:json|ya?ml|toml|plist|properties|xml)$/.test(segment); + return /(?:^|[._-])(secret|secrets|credential|credentials)(?:[._-]|$)/.test(segment) || secretBearingConfig && (/(?:^|[._-])(?:auth|oauth2?|token|tokens)(?:[._-]|$)/.test(segment) || /(?:^|[._-])(?:api|access|refresh|identity|service)[._-](?:key|token|account)(?:[._-]|$)/.test(segment) || /(?:^|[._-])firebase[._-]adminsdk(?:[._-]|$)/.test(segment)) || /\.(?:key|pem|p12|pfx|jks|keystore)$/.test(segment); }); } function hasGeneratedSegment(relativePath) { diff --git a/src/audit.ts b/src/audit.ts index bd6fbc4..7f751af 100644 --- a/src/audit.ts +++ b/src/audit.ts @@ -784,7 +784,25 @@ function normalizeRelativePath(value: string): string | null { } function isSecretLike(relativePath: string): boolean { - const segments = relativePath.toLowerCase().split("/"); + const normalized = relativePath.toLowerCase(); + const knownAuthStores = [ + ".docker/config.json", + ".config/gh/hosts.yml", + ".config/glab-cli/config.yml", + ".azure/accesstokens.json", + ]; + if ( + knownAuthStores.some( + (storePath) => + normalized === storePath || normalized.endsWith(`/${storePath}`), + ) + ) { + return true; + } + if (path.posix.basename(normalized) === "serviceaccountkey.json") { + return true; + } + const segments = normalized.split("/"); return segments.some((segment) => { if ( segment === ".env" || @@ -802,8 +820,14 @@ function isSecretLike(relativePath: string): boolean { ) { return true; } + const secretBearingConfig = + /\.(?:json|ya?ml|toml|plist|properties|xml)$/.test(segment); return ( /(?:^|[._-])(secret|secrets|credential|credentials)(?:[._-]|$)/.test(segment) || + (secretBearingConfig && + (/(?:^|[._-])(?:auth|oauth2?|token|tokens)(?:[._-]|$)/.test(segment) || + /(?:^|[._-])(?:api|access|refresh|identity|service)[._-](?:key|token|account)(?:[._-]|$)/.test(segment) || + /(?:^|[._-])firebase[._-]adminsdk(?:[._-]|$)/.test(segment))) || /\.(?:key|pem|p12|pfx|jks|keystore)$/.test(segment) ); }); diff --git a/tests/audit.test.ts b/tests/audit.test.ts index e7191e7..c8e00b1 100644 --- a/tests/audit.test.ts +++ b/tests/audit.test.ts @@ -103,6 +103,32 @@ describe("auditWorkspace", () => { '{"dependencies":{"vue":"latest"}}', ); await writeFile(path.join(workspace, "private-key.pem"), "secret"); + await mkdir(path.join(workspace, "config"), { recursive: true }); + await mkdir(path.join(workspace, ".docker"), { recursive: true }); + const sensitiveAuthAndTokenPaths = [ + "auth.json", + "oauth.json", + "token.json", + "tokens.yaml", + "config/auth-store.json", + "config/access-token.json", + "config/refresh_token.json", + "config/api-key.json", + "config/service_account.json", + "config/OAuth2.TOML", + "config/token-cache.plist", + ".docker/config.json", + "config/serviceAccountKey.json", + "config/project-firebase-adminsdk-demo.json", + ]; + await Promise.all( + sensitiveAuthAndTokenPaths.map((relativePath) => + writeFile( + path.join(workspace, relativePath), + '{"dependencies":{"secret-detector-canary":"1.0.0"}}', + ), + ), + ); await writeFile(path.join(workspace, "binary.ts"), Buffer.from([0, 1, 2])); await writeFile(path.join(workspace, "oversized.ts"), "x".repeat(9_000)); await symlink( @@ -118,6 +144,7 @@ describe("auditWorkspace", () => { expect(report.inspectedFiles).not.toEqual( expect.arrayContaining([ ".env", + ...sensitiveAuthAndTokenPaths, "ignored.ts", "private-key.pem", "binary.ts", @@ -131,9 +158,10 @@ describe("auditWorkspace", () => { generated: 1, ignored: 1, oversized: 1, - "secret-like": 2, + "secret-like": 16, symlink: 1, }); + expect(JSON.stringify(report)).not.toContain("secret-detector-canary"); }); it("never resolves a repository-provided Git shim from PATH", async () => { @@ -230,6 +258,90 @@ describe("auditWorkspace", () => { expect(signalValues(report, "testing")).toContain("jest"); }); + it("excludes tracked auth and token paths without matching unrelated names", async () => { + const workspace = await temporaryWorkspace(); + await execFileAsync("git", ["init", "-q"], { cwd: workspace }); + await mkdir(path.join(workspace, "config"), { recursive: true }); + const sensitivePaths = [ + "auth.json", + "oauth.json", + "token.json", + ".codex/auth.json", + ".docker/config.json", + "config/access-token.json", + "config/refresh_token.yaml", + "config/api-key.json", + "config/service_account.json", + "config/OAuth2.TOML", + "config/token-cache.plist", + "config/serviceAccountKey.json", + "config/project-firebase-adminsdk-demo.json", + ]; + await mkdir(path.join(workspace, ".codex"), { recursive: true }); + await mkdir(path.join(workspace, ".docker"), { recursive: true }); + await Promise.all( + sensitivePaths.map((relativePath) => + writeFile( + path.join(workspace, relativePath), + '{"dependencies":{"tracked-secret-canary":"1.0.0"}}', + ), + ), + ); + await writeFile(path.join(workspace, "author.json"), '{"name":"author"}'); + await writeFile( + path.join(workspace, "authorization.json"), + '{"name":"authorization"}', + ); + await writeFile( + path.join(workspace, "tokenizer.json"), + '{"name":"tokenizer"}', + ); + await mkdir(path.join(workspace, "src", "auth"), { recursive: true }); + await mkdir(path.join(workspace, "docs"), { recursive: true }); + await writeFile(path.join(workspace, "src", "auth", "index.ts"), "export {};\n"); + await writeFile(path.join(workspace, "src", "token-utils.ts"), "export {};\n"); + await writeFile(path.join(workspace, "docs", "AUTH.md"), "# Authentication\n"); + await execFileAsync("git", ["add", "."], { cwd: workspace }); + await execFileAsync( + "git", + [ + "-c", + "user.name=Codesemble Test", + "-c", + "user.email=test@example.invalid", + "commit", + "-qm", + "tracked privacy fixture", + ], + { cwd: workspace }, + ); + + const report = await auditWorkspace(workspace); + + expect(report.dirtyWorktree).toBe(false); + expect(report.inspectedFiles).toEqual( + expect.arrayContaining([ + "author.json", + "authorization.json", + "tokenizer.json", + "src/auth/index.ts", + "src/token-utils.ts", + "docs/AUTH.md", + ]), + ); + expect(report.inspectedFiles).not.toEqual( + expect.arrayContaining(sensitivePaths), + ); + expect(report.inspectedFileDigests?.map(({ path: file }) => file)).not.toEqual( + expect.arrayContaining(sensitivePaths), + ); + expect(report.skipped).toContainEqual({ + reason: "secret-like", + count: sensitivePaths.length, + }); + expect(JSON.stringify(report)).not.toContain("tracked-secret-canary"); + }); + it("excludes transaction history from Git candidates and dirtiness", async () => { const workspace = await temporaryWorkspace(); await execFileAsync("git", ["init", "-q"], { cwd: workspace });