Docs/contract runbook dep audit legal routes gallery gate - #1480
Merged
pope-h merged 5 commits intoJul 30, 2026
Merged
Conversation
The repository documented the pieces of a deployment across three files - CLI commands in docs/contracts/DEPLOYMENT.md, governance in UPGRADE_PROCESS.md, script configuration in contracts/deployment/README.md - but nothing gave the ordered procedure that ties them together. Adds docs/contracts/RUNBOOK.md covering, in order: the testnet/mainnet gate, deploy (build, hash, deploy, verify, init, transfer admin to multisig, record), upgrade (including what the timelock delay is actually for), WASM hash verification against the on-chain artifact, and a rollback table that states plainly which situations are recoverable and which are not. Maps each PR template Contract Upgrade Details field to the step it comes from. Cross-links the three existing documents to it. No scripts or contracts are changed.
Runs cargo audit, npm audit and pnpm audit and triages every finding by actual exposure rather than by severity label. contracts/ was audited first since a vulnerable crate compiled into on-chain code cannot be patched after deployment: zero vulnerabilities, six informational warnings that all arrive via soroban-env-host - the host-side test emulator, not the wasm32 artifact - so none reach deployed code. backend/ and frontend/ findings are ranked by reachability. The two critical advisories in both are dev-only test tooling and are the least urgent items on the list; the most urgent are a high in multer and a moderate in express that both sit in the request path. Frontend risk is concentrated in the ~30 next advisories, notably the middleware/proxy bypasses. No dependencies are changed. Every available remediation is a next major-line move, an npm audit fix --force outside a stated range, or a bump under soroban-sdk - maintainer calls, with a recommended order and the residual risk of doing nothing stated. Also records that frontend/ carries both package-lock.json and pnpm-lock.yaml while CI installs with pnpm, so npm audit there reports on a tree that never ships.
/privacy and /terms are unlinked duplicates of /privacy-policy and /terms-of-service. Both are indexable, so a search engine can currently deliver a user to a version the product does not treat as current - and which of the two binds that user is genuinely ambiguous. The substantive difference is that there isn't one: every section of /privacy and /terms is the same placeholder sentence, while the linked routes carry real policy text. They are scaffolds, not an alternative set of terms. Adds noindex and points canonical at the linked routes, so the ambiguity stops mattering to search engines while a maintainer decides which route is canonical. Deliberately does not delete or redirect - that is the maintainer decision the issue reserves. /cookies matches the short-page shape but is linked from CookieConsentBanner and has no duplicate, so it is canonical and only gets a self-referencing canonical URL.
app/design-system is an internal component gallery with no auth and no environment gating, reachable by anyone at /design-system in production and indexable there. Returns notFound() outside development. Gated on NODE_ENV rather than FeatureFlagProvider because that provider resolves client-side from the backend, so a flag would still ship the route and its markup and only hide it after hydration - a server-side check makes it a real 404. The page still renders during next build, so it keeps type-checking against the component library. Adds noindex as well.
PR_DESCRIPTION_#1316.md and PR_DESCRIPTION_#1335_#1336.md are leftover working notes from merged PRs and are not referenced anywhere.
|
@Joycejay17 is attempting to deploy a commit to the pope-h's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@Joycejay17 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! 🚀 |
7 tasks
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
Four related gaps, all documentation-or-gating work with no behavioural change to product code.
/design-systemin production. Now 404s outside development.This is a deliberately minimal pass on each. Where an issue asks for a decision that is a maintainer's to make — which legal route is canonical, whether to take a
nextmajor-line upgrade — this PR reports what it found and stops rather than choosing.Closes #1444
Closes #1445
Closes #1446
Closes #1447
Changes
#1444 — Contracts deployment and upgrade runbook
New
docs/contracts/RUNBOOK.md. The three existing documents each held part of the picture:docs/contracts/DEPLOYMENT.mddocs/contracts/UPGRADE_PROCESS.mdcontracts/deployment/README.mdNone gave the order, and none covered WASM verification, rollback limits, or the testnet/mainnet gate. The runbook covers, in sequence:
stellar contract buildoutput can vary across toolchains, so record therustcandstellarversions alongside the hash rather than expecting a reviewer to trust a bare match.The rollback section is the part I'd most want reviewed for accuracy:
initparametersinitis single-shot. Deploy fresh.The three existing docs now cross-link to it. No scripts or contracts were touched.
On validation: the acceptance criteria ask for the runbook to be followed on testnet and corrected. I have not done that — the commands are transcribed from
contracts/scripts/,deploy-soroban.shand the existing docs rather than observed against a live network, and the runbook says so in a "Validation status" section rather than implying otherwise. I've flagged the two steps most likely to need correction on a real run: the exactstellar contract fetchinvocation (the CLI surface has moved between versions) and theset_adminentrypoint name, which is not uniform across the suite. Happy to do the testnet pass and correct it in a follow-up if that's preferred over merging it as-is.#1445 — Dependency audit
New
docs/security/dependency-audit-2026-07.md.contracts/cargo auditbackend/npm auditfrontend/pnpm auditcontracts/ was audited first and is clean:
The six warnings (
derivative,paste,anyhow,rand×2,spin) are unmaintained/unsound/yanked notices, not CVEs — and all six arrive throughsoroban-env-host, the host-side test emulator, not thewasm32-unknown-unknownartifact that gets deployed:derivativeis additionally a proc-macro — compile-time only, emits no runtime code. Exposure to deployed contracts: none. Residual risk is confined to the CI/local test host.On triage by exposure rather than label — the ordering this produces is close to inverted from the severity ordering. In
backend/, both criticals (vitest/@vitest/ui,@redocly/cli) are devDependencies that never ship; the Vitest one additionally requires the UI server to be listening, whichnpm run test:cidoesn't do. Meanwhile the genuinely urgent items are a high inmulter(DoS via deeply nested field names, reachable unauthenticated on any upload endpoint) and a moderate inexpress/qs(query-string parsing sits in front of every request). Real-exposure ranking:frontend/is dominated by Next.js — roughly 30 of the 66 advisories arenextitself, patched in>= 16.2.5. The five middleware/proxy-bypass advisories are the ones that carry real user risk: if anything is gated inmiddleware.ts, a bypass there is an auth bypass. SSRF in Server Actions and rewrites matters for the same reason on an app that also talks to wallet/RPC infrastructure. Much of the remaining count (postcss,js-yaml,brace-expansion,fast-uri,vite,esbuild) is build tooling inflating the total.Nothing was upgraded, deliberately. Every available remediation is one of: a
nextmajor-line move, annpm audit fix --forceresolving outside a stated range (wsunderethers), or a transitive bump undersoroban-sdk. Per the issue's own implementation note — a dependency upgrade that silently changes behaviour in a payment path is worse than the advisory it resolved.next16.0.x → 16.2.5 touches middleware, Server Actions and caching in an app whose auth and wallet flows depend on exactly those;wsunderetherstouches RPC connectivity. These are maintainer calls. The doc gives a recommended order (nextfirst, thenmulter, thenexpress, thenws, then dev tooling last) and states the residual risk of doing nothing.I've therefore left the "fixable vulnerabilities are fixed" criterion unmet on purpose — say the word on which of the five and I'll take them in a follow-up with the full suite verified after each.
Lockfiles:
frontend/carries bothpackage-lock.json(503 KB) andpnpm-lock.yaml(360 KB). CI installs withpnpm install --frozen-lockfile, so the pnpm lockfile is what ships andpackage-lock.jsondescribes a tree that is never built. This is an auditing hazard specifically —npm auditthere reports on the wrong tree, giving both false findings and false silence. Recommend deleting it, not done here because a lockfile change deserves its own PR with a verified install and build. The repository root has the same pair.Existing tooling:
security-scan/already has a scanner/orchestrator/aggregator structure. Anything durable from this should extend that rather than duplicate it; this doc is a point-in-time snapshot.#1446 — Duplicate legal routes
The substantive difference between the versions is that there isn't one. Every section of
/privacy(59 lines) and/terms(59 lines) is the identical placeholder string:/privacy-policy(165 lines) and/terms-of-service(136 lines) carry real policy text — data collection categories, payment information handling, identity documents, liability limits. So these are not two competing sets of terms; they are scaffolds sitting next to the real documents. That makes the exposure narrower than it first looks, but not zero: the scaffolds were indexable, so a search engine could deliver a user to a page that says the policy doesn't exist yet.Added
robots: { index: false, follow: false }andalternates: { canonical: ... }pointing at the linked routes.Not removed or redirected. The issue reserves the canonical decision for a maintainer, and deletion is not reversible from a contributor's side. The noindex + canonical stops the ambiguity mattering to search engines while that decision is pending. If you confirm
/privacy-policyand/terms-of-serviceare canonical, the redirects are a two-linenext.config.mjschange I'm happy to add here./cookies: checked, and it is not orphaned. It matches the short-page shape (59 lines, all placeholder) but it is linked fromcomponents/CookieConsentBanner.tsx:27and has no longer-form duplicate — so it is the canonical cookie policy. Its content being placeholder is a content gap, not a routing one. It gets a self-referencing canonical and no noindex.Canonical metadata could not be added to
/privacy-policyand/terms-of-servicethemselves: both are"use client"components, which cannot exportmetadata. That needs alayout.tsxper route and belongs with the metadata issue.#1447 — Design system gallery
app/design-system/page.tsxnow callsnotFound()unlessNODE_ENV === "development", plusrobots: { index: false, follow: false }.Gated on
NODE_ENVrather thanFeatureFlagProvider, which the issue offered as an option.frontend/lib/featureFlags.tsstarts from static defaults and hydrates fromGET /api/config/feature-flagsafter mount — it is a client-side provider. A flag would still ship the route and its markup to production and only hide it after hydration, which is a visual gate rather than a real one. A server-componentNODE_ENVcheck makes it a genuine 404.Chose this over deleting the route because the gallery is the only thing that type-checks the component library end to end, and it still renders during
next build— so that incidental check survives./design-systemmoves from a static route toƒ(server-rendered on demand) in the build output, which is expected.How to test
Frontend CI (both green locally):
Build output confirms the gating:
├ ƒ /design-system,├ ○ /privacy,├ ○ /terms.Contracts CI: no Rust changed in this branch — only markdown under
docs/contracts/andcontracts/.cargo fmt --all -- --checkpasses.clippy --workspace --all-targets --all-featuresandtest --workspaceare unaffected by markdown-only edits.Backend CI: untouched by this branch. No dependency, endpoint or
openapi.ymlchanges.Manual checks:
/design-systemreturns 404 in a production build, renders normally undernext dev./privacyand/termsshows<meta name="robots" content="noindex, nofollow">and<link rel="canonical" href="/privacy-policy">//terms-of-service./privacy-policyand/terms-of-service;CookieConsentBannerstill resolves to/cookies.Security Considerations
The audit doc names unpatched advisories in a public repo. That's the point of the issue, and all of them are already public in the GitHub and RustSec advisory databases; nothing in the doc reveals an exposure an attacker couldn't derive from the lockfiles.
Note on commit 5
8c701865removesPR_DESCRIPTION_#1316.mdandPR_DESCRIPTION_#1335_#1336.mdfrom the repo root — leftover working notes from merged PRs, unreferenced anywhere. Unrelated to the four issues; drop that commit if you'd rather keep them.Checklist