feat(utils): linkAuth pure engine and macro vocabulary (#113 slice 1)#120
Merged
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #120 +/- ##
==========================================
+ Coverage 81.49% 81.78% +0.29%
==========================================
Files 215 223 +8
Lines 16795 17135 +340
Branches 3214 3326 +112
==========================================
+ Hits 13687 14014 +327
- Misses 3108 3121 +13
🚀 New features to boost your workflow:
|
jdutton
approved these changes
Jun 5, 2026
jdutton
left a comment
Owner
There was a problem hiding this comment.
Approve. High-quality, security-conscious foundation slice. Read all 8 engine modules, the schema, build helper, and wiring; ran the 169 isolated unit tests (all green); and independently verified the security-load-bearing properties.
Verified independently
- 169 unit tests pass (140 utils + 29 resources).
- Host-match anchoring is real — ran picomatch directly:
github.comrejectsevil-github.comandgithub.com.evil.com;*.sharepoint.comclaims subdomains but not the bare apex. safeExecResultisshell: false— the §6.1 "shell operators become literal argv" claim holds at the exec layer.- Token-never-leaks invariant in
resolve.tsis correct — resolved token wins over any regex capture namedtoken.
Strengths
- Consistent prototype-pollution discipline (
Object.create(null)+Object.hasOwneverywhere; closed allowlist viahasOwn, notin), each pinned by adversarial tests. - Clean, genuinely-pure slice boundary; thoughtful lazy macro load.
- Postel's law applied correctly (strict inline schema,
.passthrough()only on the macro-override branch).
Minor (non-blocking)
- Comment rot —
expand-macro.ts:10and the CHANGELOG entry referencescripts/copy-yaml-assets.mjs, which doesn't exist; the real helper ispackages/dev-tools/src/copy-yaml-assets.ts. Worth fixing both (CHANGELOG is still in[Unreleased]). - Test gap —
deepMergeis safe against a__proto__-as-override-key attack, but unlike every sibling module it has no adversarial test pinning it. Cheap to add (expandMacro('github', { __proto__: { polluted: true } })→ assert no global pollution). - Informational:
redactHeadersis implemented/tested but has no production call site until slice 2 — the §8 "tokens never leak" guarantee depends on that wiring landing.
Nice work.
Adds the pure-logic foundation for the linkAuth feature designed in issue #113: a config-driven engine that resolves authenticated external URLs by rewriting them to authenticatable endpoints and attaching tokens, with neither network nor filesystem dependencies in the engine itself. Eight modules under `packages/utils/src/link-auth/`: - `transforms.ts` closed allowlist (base64url, urlencode, lower) - `template.ts` tiny `${name}` / `${transform(name)}` renderer - `rewrite.ts` ordered when → vars → to pipeline - `build-headers.ts` header rendering + structural Authorization redaction - `select-provider.ts` host-glob match with excludeHost (picomatch) - `expand-macro.ts` YAML loader + deep-merge expander (lazy load) - `resolve-token.ts` ordered env / safeExecResult-backed command sources - `resolve.ts` public `resolveAuthenticatedUrl(url, config)` entry Ships `github` and `sharepoint` macros as a YAML data asset (`src/link-auth/macros.yaml`). New cross-platform post-build asset-copy lives in `packages/dev-tools/src/copy-yaml-assets.ts` and is invoked from `packages/utils/package.json`'s build script — first YAML-data-shipping pattern in the utils package. Companion Zod schema in `@vibe-agent-toolkit/resources` (`src/schemas/link-auth.ts`) validates the `resources.linkAuth` config block (strict; accepts either `{ use: <macro>, ...overrides }` or a full inline provider). Wired as an optional field on `ResourcesConfigSchema`. 140 unit tests in utils + 29 schema tests in resources, all pure-logic, no network or filesystem dependencies. Security-load-bearing tests pin the closed-allowlist guarantee, the `${__proto__}` lookup bypass defense, the token-never-leaks-into-Authorization invariant (precedence rule when a regex capture is named `token`), and `shell: false` literal-argv handling in token commands. Out of scope for this slice (deferred to subsequent slices per the design's non-goals): wiring into `ExternalLinkValidator` (slice 2), the five `LINK_AUTH_*` `CODE_REGISTRY` entries (slice 2), the content-fetch primitive + content cache (slice 3), and the cross-platform `.cmd`-shim system test (slice 4). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5046f52 to
6e0b064
Compare
- Fix stale path reference in expand-macro.ts JSDoc and CHANGELOG entry (was scripts/copy-yaml-assets.mjs; actual helper is packages/dev-tools/src/copy-yaml-assets.ts). - Pin deepMerge __proto__-as-override-key defense with an adversarial test (uses JSON.parse to construct __proto__ as a real own-property — object literal syntax is special-cased and does not exercise the path). Asserts no Object.prototype pollution and that the resulting object, having a null prototype, does not inherit polluted keys. Per jdutton review on PR #120. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
jdutton
added a commit
that referenced
this pull request
Jun 5, 2026
Resolves the CHANGELOG.md [Unreleased] conflict against #120 (linkAuth, now on main). Beyond the union, consolidated the two redundant empirical-compat-harness Internal bullets (v1 scaffold + v2 foundations, same subsystem/same release cycle) into one coherent entry preserving all facts, and ordered Internal as features → compat-empirical cluster. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Slice 1 of issue #113 (authenticated external link resolution). Adds the pure-logic foundation — config-driven engine + macros + Zod schema — with no consumer wiring yet. The engine resolves an authenticated fetch plan for a URL but doesn't issue requests, doesn't touch the existing
ExternalLinkValidator, and doesn't add codes toCODE_REGISTRY. Those land in slice 2.What's in this PR
Engine — eight modules in
packages/utils/src/link-auth/:transforms.tsbase64url,urlencode,lowertemplate.ts${name}/${transform(name)}rendererrewrite.tsbuild-headers.tsAuthorizationredactionselect-provider.tsexcludeHost(picomatch)expand-macro.tsresolve-token.tssafeExecResult-backed command sourcesresolve.tsresolveAuthenticatedUrl(url, config)entryReturns one of:
{ fetchUrl, headers }— ready to fetch{ outcome: 'unsupported' }— no provider claims, or rewrite missed{ outcome: 'unverified', reason }— claimed + rewrote, but no token resolvedMacros —
githubandsharepointship as a YAML data asset (src/link-auth/macros.yaml). New cross-platform post-build asset-copy inpackages/dev-tools/src/copy-yaml-assets.tsinvoked from utils'buildscript.Schema —
LinkAuthConfigSchema(Zod, strict) inpackages/resources/src/schemas/link-auth.ts. Wired aslinkAuth: LinkAuthConfigSchema.optional()onResourcesConfigSchema. Accepts either{ use: <macro>, ...overrides }or full inline providers.Tests — 140 unit tests in utils + 29 schema tests in resources, all pure-logic (no network, no filesystem dependencies except the loaded macros.yaml asset). Security-load-bearing tests pin:
Object.prototypekeys (toString,__proto__,constructor, etc.)${__proto__}lookup bypass defense in the template renderertoken)shell: falseliteral-argv handling in token commands (shell operators become literal argv elements, not pipes)base64url(guards against lockstep encoder/decoder regression)What's NOT in this PR (deferred per design)
ExternalLinkValidatorhealth-check branch — slice 2LINK_AUTH_*entries inCODE_REGISTRY+docs/validation-codes.md— slice 2CODE_REGISTRYentries — slice 2 (will audit existing codes for gaps when added).cmd-shim system test for token command dispatch — slice 4GIT_*env scrubbing for git-backed token commands — slice 4Notable design decisions
expand-macro.tsdefersreadFileSyncto first call. Why: other tests in the repo mocknode:fs, so eager module-init reads crash unrelated test suites. Module import is now side-effect-free.Object.create(null)everywhere. Every map produced by the engine (contexts, captures, headers, expanded macros) uses null prototypes. Belt-and-braces against__proto__-shaped keys from any source (YAML, regex captures, user config).resolve.ts, the schema inresources. Two sources of truth for the shape, kept in sync by code review. Future option to derive one from the other (probably the TS interface from the Zod schema), but not in this slice.use:form overrides are NOT strictly validated. Macro overrides can be any shape per Zod's.passthrough(). Strict validation of the merged result is post-expansion (slice 2 wires that).Test plan
bun run validatepasses locally (✅ confirmed: 14 steps, 214s)resources.linkAuth: { providers: [{ use: 'github' }] }in avibe-agent-toolkit.config.yamlparses without error (manual)expandMacro('github')returns the expected shape via a Node REPL (manual)Implements design doc in #113 (slice 1 of 4).
🤖 Generated with Claude Code