chore: dedupe shared schemas, byte-accurate password bound, CI hardening, dep-audit fixes - #160
Merged
dev-fani merged 4 commits intoAug 30, 2026
Conversation
None of ci.yml's jobs (audit, lint-and-typecheck, build, test) write to the repository, comment on PRs, or need anything beyond reading the checked-out code, yet none declared a permissions: block, so every job ran with the ambient repository/org default GITHUB_TOKEN grant. Add a top-level `permissions: contents: read` ceiling (mirroring release.yml, which already scopes itself to the single `contents: write` it needs). All jobs continue to pass unchanged. Closes fanilabs#119 Claude-Session: https://claude.ai/code/session_01BQMgSfHSVAWb8ghcvs8tsF
`password = z.string().min(8).max(72)` justified `.max(72)` with "bcrypt silently truncates beyond 72 bytes", but Zod's `.max()` counts UTF-16 code units, while bcrypt truncates at 72 *bytes*. A password using multi-byte UTF-8 characters (emoji, many non-Latin scripts) can sit under 72 JS characters while exceeding 72 bytes, so bcrypt silently truncates it earlier than the schema's own rationale assumes. Replace `.max(72)` with a `.refine()` on `Buffer.byteLength(value, 'utf8')`. `resetPasswordBodySchema.newPassword` reuses the same `password` constant, so registration, login and password-reset all pick up the corrected bound. Add a schema unit test covering the emoji-under-72-chars-over-72-bytes case plus the exactly-72-ASCII-bytes accepted case. Closes fanilabs#121 Claude-Session: https://claude.ai/code/session_01BQMgSfHSVAWb8ghcvs8tsF
…Schema to shared
`chainDeliveryId`/`chainFleetId` (the `/^\d+$/` digit-string pattern) and
`transactionResponseSchema` (`{ data: { xdr } }`) were redefined
character-for-character across the deliveries, disputes, escrow, fleet and
reputation modules' `interface/schemas.ts` — the same class of duplication
already fixed for `stellarAddress` (src/shared/validation/stellar-address.ts).
Add `src/shared/validation/chain-id.ts` (exports `chainId`, generically
named since it backs both `chainDeliveryId` and `chainFleetId`) and
`src/shared/validation/transaction-response.ts`. Each module now aliases
`chainId` and re-exports `transactionResponseSchema` from shared instead of
defining its own copy; route modules importing `transactionResponseSchema`
from `./schemas.js` are unaffected. Module-specific fragments
(`evidenceHash`, `amount`, `senderShareBps`, …) are left exactly where they
are. `interface/` importing from `shared/` is permitted by the existing
eslint-plugin-boundaries rules. Pure extraction, no behaviour change.
Closes fanilabs#122
Claude-Session: https://claude.ai/code/session_01BQMgSfHSVAWb8ghcvs8tsF
`pnpm audit` reported 31 advisories (3 critical, 17 high) against the committed lockfile, none caught or remediated by any process: - vitest ^2.1.4 resolves below the patched line for GHSA-5xrq-8626-4rwp (critical — arbitrary file read/execute via the Vitest UI server). - tar (critical + several high path-traversal advisories) via bcrypt > @mapbox/node-pre-gyp > tar. - handlebars (critical JS-injection via AST type confusion) via eslint-plugin-boundaries. Changes: - Bump vitest / @vitest/coverage-v8 to ^3.2.7 (staying on 3.x; no config changes needed for vitest.config.ts). - Add pnpm.overrides pinning forward-patched tar, handlebars, vite, esbuild, nanoid, js-yaml and fast-uri — all reached only through build/lint/test tooling. - Document the single residual advisory (GHSA-w5hq-g745-h8pq, uuid via the dev-only autocannon load-tester, below the `high` CI threshold, no fix available upstream) in docs/SECURITY.md. `pnpm audit --audit-level=high` now reports zero findings (was 20 high/critical). `pnpm test` / `pnpm build` behaviour is unchanged by the vitest bump. Closes fanilabs#120 Claude-Session: https://claude.ai/code/session_01BQMgSfHSVAWb8ghcvs8tsF
|
@willi-d7 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
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
Backlog cleanup addressing four independent issues — a schema-duplication refactor, a password-validation correctness fix, a CI hardening change, and a dependency-audit remediation. Each is a separate commit.
modules/*/interface/schemas.tssrc/shared/validation/modules/auth.github/workflows/ci.ymlpermissions:blockpackage.json/pnpm-lock.yaml#122 — Deduplicate
chainId/transactionResponseSchema(refactor,technical-debt)chainDeliveryId/chainFleetId(the/^\d+$/digit-string pattern) andtransactionResponseSchema({ data: { xdr } }) were redefined character-for-character across the deliveries, disputes, escrow, fleet and reputation modules — the same duplication already fixed forstellarAddress(src/shared/validation/stellar-address.ts).src/shared/validation/chain-id.ts— exportschainId, named generically because the identical shape backs bothchainDeliveryIdandchainFleetId.src/shared/validation/transaction-response.ts— exportstransactionResponseSchema.chainIdand re-exportstransactionResponseSchemafrom the shared module instead of defining its own copy. Route modules that importtransactionResponseSchemafrom./schemas.jsare unaffected.evidenceHash,amount,senderShareBps, …) are left exactly where they are — this is scoped only to the fragments proven identical.interface/importing fromshared/is already permitted by theeslint-plugin-boundariesrules (eslint.config.js).Pure extraction, no behaviour change.
#121 — Password length bounded by bytes, not characters (
security,validation)password = z.string().min(8).max(72)justified.max(72)with "bcrypt silently truncates beyond 72 bytes", but Zod's.max()counts UTF-16 code units while bcrypt truncates at 72 bytes. A password using multi-byte UTF-8 characters (emoji, many non-Latin scripts) can sit under 72 JS characters while exceeding 72 bytes — so two distinct such passwords that differ only after their first 72 bytes hash identically..max(72)with.refine(v => Buffer.byteLength(v, 'utf8') <= 72, …)and updates the inline comment.resetPasswordBodySchema.newPasswordreuses the samepasswordconstant, so registration, login and password-reset all pick up the corrected bound.src/modules/auth/interface/schemas.spec.ts: an emoji password under 72 JS chars but over 72 UTF-8 bytes is now rejected with a clear message; a 72-byte pure-ASCII password is still accepted.#119 — Least-privilege
permissions:in ci.yml (ci,security,devops)None of ci.yml's jobs write to the repo, comment on PRs, or need more than read access to the checked-out code, yet none declared a
permissions:block — every job ran with the ambient defaultGITHUB_TOKENgrant. Adds a top-levelpermissions: contents: readceiling, mirroringrelease.yml(which already scopes itself to the singlecontents: writeit needs). All jobs pass unchanged.#120 — Dependency-audit remediation (
dependencies,security)pnpm auditreported 31 advisories (3 critical, 17 high) against the committed lockfile:^2.1.4resolves below the fix forGHSA-5xrq-8626-4rwp(critical — arbitrary file read/execute via the Vitest UI server).bcrypt > @mapbox/node-pre-gyp > tar.eslint-plugin-boundaries.Changes:
vitest/@vitest/coverage-v8to^3.2.7(staying on 3.x per the issue; novitest.config.tschanges needed).pnpm.overridespinning forward-patched tar, handlebars, vite, esbuild, nanoid, js-yaml, fast-uri — all reached only through build/lint/test tooling.docs/SECURITY.md:GHSA-w5hq-g745-h8pq(uuid, reached only via the dev-onlyautocannonload-tester, moderate so below the--audit-level=highCI gate, no upstream fix available).Audit before/after:
Testing
pnpm audit --audit-level=high→ clean (exit 0), was 20 high/critical.src/modules/auth/interface/schemas.spec.ts— 4 tests pass.prettier --checkandeslintclean on all changed files.pnpm test/pnpm typecheck/pnpm build/pnpm lintshow no new failures introduced by this branch (verified against a cleanupstream/mainbaseline — the pre-existing failures onmainin unrelated modules are untouched and out of scope here).Closes #122
Closes #121
Closes #120
Closes #119