Shipkit
DocsFeaturesPricing
DocsFeaturesPricing

Shipkit DocumentationBuild memory optimizationDevelopment GuideEnvironment VariablesError HandlingFile StructureMiddlewareRebranding Guide

Vercel Deployment Checks

Development GuideCLIDependency UpdatesDevToolsFeature FlagsAI PromptsSetup RequirementsSupply chaintRPCWeb Workers

FeaturesAIAnalytics IntegrationAuthenticationCMSDatabaseEmailHapticsPaymentsComponent RegistryStorageUIVisual BuilderWaitlist Feature

Quick StartDeploymentEnvironment VariablesFile StructureSetup Wizard

Caching & Rate LimitingContent ManagementError HandlingMarkdown and MDXMiddlewareProxy-Safe Server ActionsRebranding GuideWebhook Security Implementation GuideMulti-Zone ArchitectureVercel Deployment

IntegrationsPayload CMSDataFast Analytics IntegrationGoogle Analytics IntegrationGoogle Tag Manager IntegrationPostHogStatSig IntegrationUmami Analytics IntegrationAuth.js IntegrationBetter AuthClerkStack Auth IntegrationSupabase Authentication IntegrationBuilder.io IntegrationPayload CMS IntegrationAWS S3 IntegrationResend Email IntegrationUpstash Redis IntegrationVercel Blob IntegrationLemon SqueezyPolarStripe

API RoutesComponentsAPI Route SnippetsAuthentication SnippetsComponent SnippetsForm Handling SnippetsSnippets Introduction

    Supply chain

    Shipkit is upstream of every site built from it, so its dependency tree is part of the product. A flaw here is inherited by every downstream deployment, and a fix here is worthless until it reaches them.

    This page is the operating process. It exists because the failure mode is not missing the problem. Four separate systems found the September 2026 advisories. It is never closing it.

    The ranking rule

    Severity alone is the wrong trigger. A critical in vitest browser mode never reaches a user; a critical in the auth layer is the front door. Rank by reachability × exploitability.

    TierTestClock
    0Ships to production and an outsider can reach it: auth bypass, injection, RCE, traversal, SSRF, deserializationSame day
    1Ships to production, but the impact is availability onlyThe next Monday window
    2Only ever executes on a developer machine or a build runnerMonthly sweep
    3Moderate and lowRecorded, never paged

    Tier 2 is not "ignore it". A build runner holds deploy credentials and is a real target; it just is not an emergency.

    A Tier 0 finding outranks whatever is on the board. This is the only part of the process that needs enforcing rather than scheduling. Without it, urgency loses to roadmap and the queue refills.

    Running the triage

    bun run audit:triage
    

    This ranks the current tree and exits non-zero when anything is in Tier 0, so it works as a gate. It re-checks every advisory against the version actually installed, because bun audit does not. It will report a Next.js 15 advisory against a Next.js 16 tree.

    Use two sources and trust neither alone. GitHub's Dependabot alerts do proper version-range matching against the dependency graph; bun audit catches advisories GitHub has not indexed yet. In September 2026 GitHub reported 24 alerts here and bun audit found two more that GitHub did not list.

    The week

    WhenWhatOwner
    Daily 07:00Scan every repo, rank, report only what is newautomated
    Monday 09:00The upgrade window: one batch, minors and patches together, majors one at a timeDRI
    Monday 09:45Propagate upstream fixes to shipkit-www and client deploymentsDRI
    First MondayTier 2 sweep, prune unused packages, unblock or cancel anything blocked over 14 daysDRI

    The daily scan is sh.lacy.supply-chain. It stays silent unless a new critical or high appeared, and files a board task assigned to the Security Engineer when one does. A digest nobody reads is worse than silence.

    Anything unmerged after two upgrade windows is either landed or explicitly rejected with a reason on the ticket. Nothing sits in review quietly.

    The merge gate

    bash scripts/verify.sh
    

    Runs typecheck, lint, unit and node tests, a production build, and route smoke checks, then prints a summary to paste into the PR. While GitHub Actions is unavailable, a passing verify summary in the PR body is the gate.

    Never merge a dependency bump without one. Eleven dependency PRs queued up in September 2026 precisely because nothing required it, and two agents ended up opening duplicates of each other's work.

    Compromise is not the same as vulnerability

    A vulnerability is an honest bug with an advisory and a patch. A compromise is a package that was taken over: a hijacked maintainer account, a typosquat, a postinstall script added in a patch release. Dependabot will not catch it, because at the moment it matters there is no advisory yet.

    • Install scripts are denied by default. Keep trustedDependencies short and justified; every entry is a package allowed to execute code on install.
    • Review lockfile changes on their own, never bundled into a feature PR. A one-line resolution change is how this arrives.
    • Use --frozen-lockfile in CI and on every unattended install.
    • Treat a dormant package publishing at an odd hour as suspicious until checked.

    Version constraints are part of the problem

    In September 2026 better-auth sat on a critical for weeks with a patch available. The range was ~1.5.6, and a tilde cannot reach 1.6.11. Nothing was broken; the constraint was simply never revisited.

    Pin exactly where you mean to pin, and write down why. A tilde or an exact pin on a security-relevant package is a standing decision to not receive fixes, so it needs a reason next to it.

    Transitive fixes go through overrides

    Most remaining advisories sit in packages nothing declares directly, so the only lever is a top-level overrides entry. Pin to the lowest patched version inside the current major wherever one exists, so the override closes the advisory without dragging in a breaking change.

    Two of the current entries are worth knowing about:

    • isolated-vm is pinned to ^7.0.1. It arrives through @builder.io/react, and 6.x cannot compile against the V8 API in Node 26, which broke every bun install that ran install scripts. 7.0.1 builds and ships prebuilds.
    • sharp is pinned to ^0.35.4 because @huggingface/transformers asks for ^0.34.1, and a caret on a 0.x range cannot reach the patched line.

    Check that an override actually took effect. node -e "console.log(require('./node_modules/<pkg>/package.json').version)" is the only reliable confirmation, and a stale nested copy under another package's node_modules will silently keep the old version even after the lockfile updates.

    Overrides

    Bun reads top-level overrides and resolutions. It does not read pnpm.overrides. There is a pnpm.overrides block in package.json that has never applied here; check the resolved version with node -e "console.log(require('./node_modules/<pkg>/package.json').version)" before believing an override took effect.