Connect a JavaScript / Node.js application to Patchstack for continuous vulnerability monitoring. Scans your package-lock.json and reports installed packages so Patchstack can match them against its vulnerability database and notify you when something needs patching.
For how this repo fits with the wider Patchstack ecosystem (saas, hub, patchstack-website, patchstack-connect), see patchstack/saas → docs/ecosystem.md.
Copy this request into a coding assistant, or run the same command yourself:
I have vetted
@patchstack/connectand authorize its widget and production build hooks. Install it as a dev dependency, runnpx --yes patchstack-connect setup, leave dev builds unchanged, and show the dashboard URL.
setup is state-aware and idempotent: it scans dependencies, provisions or reuses the site, manages the disclosure widget, wires the existing build command without replacing it, and prints the remaining setup status. It never runs the project build or the opt-in protect command. guide provides the same project-specific status without changing files.
npm install --save-dev @patchstack/connect && npx @patchstack/connect setupUse your project's own package manager. On Bun-managed projects (including many Lovable projects) install with
bun add -d @patchstack/connectinstead — runningnpm installthere plants apackage-lock.jsonthat the platform's native dependency flow never updates again, leaving a stale lockfile next to the live one. The connector detects and works around that (see Stale lockfiles below), but not creating the fossil is better.
That's it. setup:
- Reads your lockfile (see Supported lockfiles).
- POSTs the package list to Patchstack with no UUID.
- Patchstack provisions a fresh site and returns its UUID.
- The connector writes the UUID to
.patchstackrc.jsonso the nextscantargets the same site. - The connector installs the disclosure widget's
<script>tag into your root HTML shell (see The disclosure widget below) so the "Report a vulnerability" button shows up on the next preview reload. - Wires
scanbefore builds andmark-buildafter builds, preserving existing commands and using direct build chaining for Bun. - Prints a dashboard link — open it in a browser to attach the new site to your Patchstack account. You can re-display it any time with
npx @patchstack/connect status.
If you already created an "Application" site in the Patchstack dashboard, pre-seed the UUID:
npm install --save-dev @patchstack/connect
npx @patchstack/connect init <your-site-uuid>
npx @patchstack/connect setuppatchstack-connect scan [options] Scan the lockfile and POST to Patchstack.
If no UUID is configured the server provisions
one and the connector persists it. After a
successful post, adds/updates the disclosure
widget tag in the root HTML shell (opt out
with "widget": false in .patchstackrc.json)
patchstack-connect setup [options] Run scan, manage the widget, and idempotently
wire package.json build scripts. Never runs
the project build or protect
patchstack-connect init <site-uuid> Optional: pre-seed .patchstackrc.json with
an existing site UUID
patchstack-connect status [options] Show current configuration
patchstack-connect mark-build [options] Stamp built HTML with a production flag +
build fingerprint and ensure the widget tag
in built pages (run as a postbuild step)
patchstack-connect guide Show this project's setup status (what's done,
what's missing, with tailored commands), then
print the full setup guide
patchstack-connect protect Opt-in: install the always-on runtime exploit
guard. Auto-wires supported server stacks;
use --check to verify or --demo for local rules.
Never run by scan/setup/guide/mark-build.
patchstack-connect demo node-serialize Production-backed walkthrough: require
node-serialize@0.0.4, scan it, wait for live
rule 18843, install + verify the runtime guard,
and print exploit/benign test requests.
patchstack-connect demo-guide node-serialize Read-only, state-aware instructions for the
local demo, including the next exact command,
expected proof, and cleanup.
patchstack-connect help Print help
Options (for scan, setup, and status):
--site-uuid <uuid> Override the configured site UUID
--endpoint <url> Override the API endpoint
--dry-run (scan only) Print the payload without posting
Options (for demo and demo-guide):
--url <url> Test endpoint printed at the end
(default: http://localhost:3000/api/tasks)
Precedence (highest wins):
- CLI flag (
--site-uuid,--endpoint) - Environment variable
.patchstackrc.jsonin the current directory
Environment variables:
PATCHSTACK_SITE_UUID— the site UUID from your Patchstack dashboardPATCHSTACK_ENDPOINT— override the API endpoint (defaulthttps://api.patchstack.com/monitor/pulse/manifest)PATCHSTACK_TIMEOUT_MS— request timeout in milliseconds (default30000)
.patchstackrc.json example:
{
"siteUuid": "550e8400-e29b-41d4-a716-446655440000",
"widget": true
}"widget" is optional and defaults to true; set it to false to stop the connector from managing the disclosure-widget tag (see The disclosure widget).
The site UUID identifies the site; it is not a secret — the disclosure widget ships the same UUID in client-side HTML, and committing .patchstackrc.json is the intended workflow so every developer and CI run reports to the same site. Possession of the UUID lets someone submit dependency manifests for that site (noise, not data access). In CI setups where the file isn't committed, set PATCHSTACK_SITE_UUID instead.
The node-serialize scenario demonstrates dependency detection and a live, version-scoped virtual patch against a throwaway Express application. Connect/provision the project first, deliberately add the known-vulnerable package, then run:
npm install --save-exact node-serialize@0.0.4
npx @patchstack/connect demo node-serializeThe demo command does not install the vulnerable package. It verifies the exact version in the lockfile, posts the production npm manifest to the configured site, polls the corresponding Pulse rules endpoint for rule 18843, runs the normal protect installer, checks that the guard is wired, and prints one exploit request plus one benign control request. It never starts or restarts the application and never sends either test request itself.
For a read-only walkthrough that can be run before or during the demo, use:
npx @patchstack/connect demo-guide node-serializeThe guide inspects the Host-created site configuration and lockfile, explains that no deployment is required, shows the complete prepare → run → restart → prove → clean-up sequence, and ends with the next exact command for the project's current state. Pass the same --url option when the test endpoint differs from the default.
Use --url http://localhost:PORT/api/tasks when the app does not use the default http://localhost:3000/api/tasks. Remove the deliberately vulnerable dependency after the walkthrough.
The widget is a floating "Report a vulnerability" button — a disclosure channel for anyone who spots a bug on the site. The connector manages its install so the UUID never has to be copied by hand:
-
scan(after a successful post) adds this managed tag to the first root HTML shell it finds —index.html,public/index.html, orsrc/app.html— immediately before</body>:<script src="https://cdn.patchstack.com/patchstack-widget.js" data-site-uuid="<SITE_UUID>" defer data-patchstack-connect-widget="true"></script>
Re-runs update the tag in place (the
data-patchstack-connect-widgetattribute marks it as connector-managed); a pre-existing manual widget tag is left untouched.--dry-runand failed posts never edit anything. Projects whose root layout is code rather than HTML (Next.js, Nuxt, Astro, …) get the exact snippet and target file printed instead —guideshows framework-specific placement. -
mark-buildensures the same tag in built HTML output, covering builds whose source shell the connector couldn't edit, and stampswindow.__PATCHSTACK_PROD__so the widget hides the claim/login UI on the published site (owners reach it by appending#patchstackto the live URL). -
Opting out: persist
"widget": falsein.patchstackrc.jsonto disable both passes (dependency scanning only). Without it, the next successful scan re-adds the managed tag.
import { scanAndReport } from '@patchstack/connect';
const result = await scanAndReport();
console.log(result.response.stored ? 'Reported' : 'Unchanged');Lower-level pieces are also exported: scanLockfile, buildWirePayload, postManifest, resolveConfig.
{
"ecosystem": "npm",
"packages": [
{ "name": "axios", "version": "1.6.0" },
{ "name": "lodash", "version": "4.17.15" },
{ "name": "lodash", "version": "4.17.21" }
]
}That's the entire payload. No source code, no environment variable values, no file paths — just the package names and versions from your lockfile. Duplicate names with different versions are preserved so transitive vulnerabilities aren't missed. (mark-build separately stamps built HTML with a stack descriptor that may include hosting-related env variable names — e.g. VERCEL — never their values.)
- ✅
package-lock.json(npm v6 / v2 / v3) — parsed directly - ✅
pnpm-lock.yaml(pnpm v5 / v6 / v7 / v8 / v9) — parsed directly - ✅
yarn.lock(yarn classic v1 and yarn berry v2+) — parsed directly - ✅
bun.lockb(binary) — package list resolved by walkingnode_modules/ - ✅
bun.lock(text) — same fallback; direct parsing coming
If both a Bun lockfile and node_modules/ are present, the connector walks node_modules/ to enumerate the installed packages. Run bun install (or npm install) before scanning so the directory is populated.
Every scanned source is validated against package.json: if the chosen lockfile is missing dependencies that package.json declares, it is treated as a fossil (e.g. a package-lock.json created by a one-off npm install in a bun-managed project) and the connector falls through to the next source — ultimately walking node_modules/, the installed truth — and prints a warning naming the stale file. Delete the stale lockfile to silence the warning. Without this, the manifest and the build fingerprint would silently freeze while the real dependency set drifts.
npm install
npm run typecheck
npm test
npm run buildTo post the current lockfile manifest to a local Patchstack API endpoint and provision a new site:
bun run test:manifest -- --endpoint http://localhost:8000/monitor/pulse/manifestThe response should include the new site UUID. To re-test an existing site, pass that UUID explicitly:
bun run test:manifest -- --endpoint http://localhost:8000/monitor/pulse/manifest --site-uuid YOUR_REAL_UUIDUse --dry-run to preview the payload without posting.
Pull requests run typecheck, tests, build, package verification, and a production dependency audit in GitHub Actions.
Publishing runs when a GitHub Release is published. The release tag must match the package version in package.json with a leading v. For example, package.json version 0.2.0 must be released with tag v0.2.0; otherwise the workflow fails before publishing.
To publish a release:
- Bump the package version, for example
npm version 0.2.0 --no-git-tag-version. - Commit
package.jsonandpackage-lock.json. - Merge the version bump to
main. - Create and publish a GitHub Release tagged
v0.2.0. - The
Publishworkflow verifies the package, then runsnpm publish --provenance --access public.
Before the first release, configure npm trusted publishing for this package:
- Merge
.github/workflows/publish.ymltomain. - Open the
@patchstack/connectpackage settings on npmjs.com. - In Trusted publishing, choose GitHub Actions.
- Configure:
- Organization/user:
patchstack - Repository:
connect - Workflow filename:
publish.yml - Environment name:
npm
- Organization/user:
- In GitHub repository settings, create an
npmenvironment. Optional but recommended: require reviewer approval for that environment.
Do not add an npm publish token to GitHub secrets for this workflow. Trusted publishing uses GitHub OIDC short-lived credentials. After the first trusted publish succeeds, npm recommends setting package publishing access to require two-factor authentication and disallow tokens.
MIT