@patchstack/connect 0.5.5 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT-INSTALL.md +1 -1
- package/README.md +1 -0
- package/dist/cli.js +25 -15
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +5 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -3
- package/dist/index.d.ts +17 -3
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/AGENT-INSTALL.md
CHANGED
|
@@ -120,7 +120,7 @@ This is a request, not a mechanism: nothing in the install depends on it. Do it
|
|
|
120
120
|
|
|
121
121
|
**Bun-managed projects:** `bun run` does not execute npm-style `pre`/`post` scripts, so wire the build script directly instead: `"build": "patchstack-connect scan && <existing build command> && patchstack-connect mark-build"`.
|
|
122
122
|
|
|
123
|
-
3. **Verify the disclosure widget** — a floating "Report a vulnerability" button. `scan` installs it automatically into a plain HTML shell, and `mark-build` carries it into built HTML. Only when `scan` reported that it found no editable shell
|
|
123
|
+
3. **Verify the disclosure widget** — a floating "Report a vulnerability" button. `scan` installs it automatically into a plain HTML shell **or a JSX root** (Next, Remix, React Router, TanStack Start, Gatsby), and `mark-build` carries it into built HTML. Only when `scan` reported that it found no editable shell at all — a root whose head mechanism is not a plain script tag, e.g. Nuxt's `useHead` or an Astro layout — add the one-liner it printed to the root layout yourself, just before `</body>` (never a JS entry point), reading `siteUuid` from `.patchstackrc.json`. On those same roots the widget also needs the production marker above the tag — `scan` adds it automatically to a JSX root, and prints it to paste when it finds no anchor. A server-rendered site without the marker serves the build-mode claim flow to its visitors:
|
|
124
124
|
|
|
125
125
|
```html
|
|
126
126
|
<script src="https://cdn.patchstack.com/patchstack-widget.js" data-site-uuid="<SITE_UUID>" defer></script>
|
package/README.md
CHANGED
|
@@ -306,6 +306,7 @@ The widget is a floating "Report a vulnerability" button — a disclosure channe
|
|
|
306
306
|
<script src="https://cdn.patchstack.com/patchstack-widget.js" data-site-uuid="<SITE_UUID>" defer data-patchstack-connect-widget="true"></script>
|
|
307
307
|
```
|
|
308
308
|
|
|
309
|
+
- **`scan`** installs the widget tag into a plain HTML shell, and — where there is none — into a JSX root (`src/routes/__root.tsx`, `app/layout.tsx`, …), just before `</body>`. The same tag serves both: JSX reads `defer` as a boolean attribute and passes `data-*` through. A server-rendered app has no HTML shell at all, so without this its published site carries no widget and Patchstack never hears from the live page.
|
|
309
310
|
- **`scan`** also adds the production marker when the root shell is JSX rather than HTML (`src/routes/__root.tsx`, `app/layout.tsx`, …), above the widget tag and guarded by the framework's production expression. A server-rendered app emits no built HTML for `mark-build` to stamp, so without it the widget reads the published site as build mode and shows the claim flow to visitors instead of the report form.
|
|
310
311
|
|
|
311
312
|
Re-runs update the tag in place (the `data-patchstack-connect-widget` attribute marks it as connector-managed); a pre-existing manual widget tag is left untouched. `--dry-run` never edits anything; a failed post still skips the widget tag (it needs the site UUID) but the production marker may already have been written, since it runs before the post. Projects whose root layout is code rather than HTML (Next.js, Nuxt, Astro, …) get the exact snippet and target file printed instead — `guide` shows framework-specific placement.
|
package/dist/cli.js
CHANGED
|
@@ -2513,12 +2513,16 @@ function productionGate(framework) {
|
|
|
2513
2513
|
function hasJsxShell(framework) {
|
|
2514
2514
|
return framework !== null && JSX_SHELL_FRAMEWORKS.has(framework);
|
|
2515
2515
|
}
|
|
2516
|
-
function buildSourceMarkerSnippet(framework) {
|
|
2516
|
+
function buildSourceMarkerSnippet(framework, checksum = null) {
|
|
2517
2517
|
const gate = productionGate(framework);
|
|
2518
|
+
const statements = ["window.__PATCHSTACK_PROD__=true;"];
|
|
2519
|
+
if (checksum !== null && checksum !== "") {
|
|
2520
|
+
statements.push(`window.__PATCHSTACK_BUILD__=${JSON.stringify(checksum)};`);
|
|
2521
|
+
}
|
|
2518
2522
|
return `{${gate} && (
|
|
2519
2523
|
<script
|
|
2520
2524
|
${MARKER_ATTR}="true"
|
|
2521
|
-
dangerouslySetInnerHTML={{ __html: '
|
|
2525
|
+
dangerouslySetInnerHTML={{ __html: '${statements.join("")}' }}
|
|
2522
2526
|
/>
|
|
2523
2527
|
)}`;
|
|
2524
2528
|
}
|
|
@@ -2581,7 +2585,7 @@ function findJsxShellAnchor(source, tagName) {
|
|
|
2581
2585
|
}
|
|
2582
2586
|
return null;
|
|
2583
2587
|
}
|
|
2584
|
-
function ensureMarkerInJsxShell(source, framework) {
|
|
2588
|
+
function ensureMarkerInJsxShell(source, framework, checksum = null) {
|
|
2585
2589
|
if (!hasJsxShell(framework)) {
|
|
2586
2590
|
return { source, action: "unsupported" };
|
|
2587
2591
|
}
|
|
@@ -2589,7 +2593,7 @@ function ensureMarkerInJsxShell(source, framework) {
|
|
|
2589
2593
|
if (stripped.includes(PROD_MARKER_GLOBAL)) {
|
|
2590
2594
|
return { source: stripped, action: "manual" };
|
|
2591
2595
|
}
|
|
2592
|
-
const block = (indent) => [REGION_OPEN, ...buildSourceMarkerSnippet(framework).split("\n"), REGION_CLOSE].map((line) => `${indent}${line}`).join("\n");
|
|
2596
|
+
const block = (indent) => [REGION_OPEN, ...buildSourceMarkerSnippet(framework, checksum).split("\n"), REGION_CLOSE].map((line) => `${indent}${line}`).join("\n");
|
|
2593
2597
|
for (const tagName of ["head", "body"]) {
|
|
2594
2598
|
const anchor = findJsxShellAnchor(stripped, tagName);
|
|
2595
2599
|
if (anchor === null) continue;
|
|
@@ -2604,13 +2608,13 @@ ${block(indent)}${separator}${remainder}`,
|
|
|
2604
2608
|
}
|
|
2605
2609
|
return { source: stripped, action: "no-anchor" };
|
|
2606
2610
|
}
|
|
2607
|
-
function ensureSourceMarker(cwd, shell, framework) {
|
|
2611
|
+
function ensureSourceMarker(cwd, shell, framework, checksum = null) {
|
|
2608
2612
|
if (shell === null) {
|
|
2609
2613
|
return { shell: null, source: "", action: "no-anchor" };
|
|
2610
2614
|
}
|
|
2611
2615
|
const file = path9.resolve(cwd, shell);
|
|
2612
2616
|
const before = readFileSync2(file, "utf8");
|
|
2613
|
-
const result = ensureMarkerInJsxShell(before, framework);
|
|
2617
|
+
const result = ensureMarkerInJsxShell(before, framework, checksum);
|
|
2614
2618
|
if (result.source !== before) {
|
|
2615
2619
|
writeFileSync(file, result.source);
|
|
2616
2620
|
}
|
|
@@ -5051,12 +5055,15 @@ function findSourceShell(cwd) {
|
|
|
5051
5055
|
}
|
|
5052
5056
|
return null;
|
|
5053
5057
|
}
|
|
5054
|
-
function ensureSourceWidget(cwd, siteUuid) {
|
|
5055
|
-
const shell = findSourceShell(cwd);
|
|
5058
|
+
function ensureSourceWidget(cwd, siteUuid, jsxShell = null) {
|
|
5059
|
+
const shell = findSourceShell(cwd) ?? jsxShell;
|
|
5056
5060
|
if (shell === null) {
|
|
5057
5061
|
return { shell: null, action: "no-shell" };
|
|
5058
5062
|
}
|
|
5059
5063
|
const file = path11.join(cwd, shell);
|
|
5064
|
+
if (!existsSync12(file)) {
|
|
5065
|
+
return { shell: null, action: "no-shell" };
|
|
5066
|
+
}
|
|
5060
5067
|
const before = readFileSync9(file, "utf8");
|
|
5061
5068
|
const { html, action } = ensureWidgetInHtml(before, siteUuid);
|
|
5062
5069
|
if (html !== before) {
|
|
@@ -8927,8 +8934,9 @@ async function runScan(args, options = {}) {
|
|
|
8927
8934
|
const cleared = applyBuildStamp(process.cwd(), null);
|
|
8928
8935
|
if (cleared.kind === "cleared") console.log(`Removed the previous map binding from ${cleared.file}.`);
|
|
8929
8936
|
}
|
|
8937
|
+
const shellFramework = detectStack(payload.packages).framework;
|
|
8930
8938
|
if (config.widget) {
|
|
8931
|
-
reportSourceMarker(
|
|
8939
|
+
reportSourceMarker(shellFramework, computeManifestChecksum(payload.packages));
|
|
8932
8940
|
}
|
|
8933
8941
|
const provisioning = config.siteUuid === null;
|
|
8934
8942
|
if (provisioning) {
|
|
@@ -8974,7 +8982,7 @@ async function runScan(args, options = {}) {
|
|
|
8974
8982
|
const connected = response.claim?.state === "claimed" || response.claim?.state === "owned-by-you";
|
|
8975
8983
|
const effectiveUuid = config.siteUuid ?? response.uuid ?? null;
|
|
8976
8984
|
if (config.widget && effectiveUuid !== null && effectiveUuid.length > 0) {
|
|
8977
|
-
reportSourceWidget(effectiveUuid);
|
|
8985
|
+
reportSourceWidget(effectiveUuid, shellFramework);
|
|
8978
8986
|
}
|
|
8979
8987
|
const linkUuid = response.uuid ?? config.siteUuid;
|
|
8980
8988
|
if (!connected && (provisioning || claimLines.length > 0) && linkUuid !== null && linkUuid !== void 0 && linkUuid.length > 0) {
|
|
@@ -9005,9 +9013,11 @@ async function runScan(args, options = {}) {
|
|
|
9005
9013
|
}
|
|
9006
9014
|
return 0;
|
|
9007
9015
|
}
|
|
9008
|
-
function reportSourceWidget(siteUuid) {
|
|
9016
|
+
function reportSourceWidget(siteUuid, framework) {
|
|
9009
9017
|
try {
|
|
9010
|
-
const
|
|
9018
|
+
const hint = hasJsxShell(framework) ? resolveWidgetFileHint(process.cwd(), framework) : null;
|
|
9019
|
+
const jsxShell = hint !== null && !hint.toLowerCase().endsWith(".html") ? hint : null;
|
|
9020
|
+
const result = ensureSourceWidget(process.cwd(), siteUuid, jsxShell);
|
|
9011
9021
|
switch (result.action) {
|
|
9012
9022
|
case "added":
|
|
9013
9023
|
console.log(`Widget: added the "Report a vulnerability" tag to ${result.shell}. Reload your preview to see it.`);
|
|
@@ -9026,7 +9036,7 @@ function reportSourceWidget(siteUuid) {
|
|
|
9026
9036
|
console.log(` ${buildWidgetTag(siteUuid)}`);
|
|
9027
9037
|
break;
|
|
9028
9038
|
case "no-shell":
|
|
9029
|
-
console.log("Widget: no
|
|
9039
|
+
console.log("Widget: no root shell found to edit (index.html / public/index.html / src/app.html, or a JSX root).");
|
|
9030
9040
|
console.log("Add this tag to your root layout before </body> (run `guide` for framework-specific placement):");
|
|
9031
9041
|
console.log(` ${buildWidgetTag(siteUuid)}`);
|
|
9032
9042
|
break;
|
|
@@ -9038,13 +9048,13 @@ function reportSourceWidget(siteUuid) {
|
|
|
9038
9048
|
console.warn(`Widget: skipped (${err.message}).`);
|
|
9039
9049
|
}
|
|
9040
9050
|
}
|
|
9041
|
-
function reportSourceMarker(framework) {
|
|
9051
|
+
function reportSourceMarker(framework, checksum = null) {
|
|
9042
9052
|
try {
|
|
9043
9053
|
const shell = resolveWidgetFileHint(process.cwd(), framework);
|
|
9044
9054
|
if (shell === null || shell.toLowerCase().endsWith(".html")) {
|
|
9045
9055
|
return;
|
|
9046
9056
|
}
|
|
9047
|
-
const result = ensureSourceMarker(process.cwd(), shell, framework);
|
|
9057
|
+
const result = ensureSourceMarker(process.cwd(), shell, framework, checksum);
|
|
9048
9058
|
switch (result.action) {
|
|
9049
9059
|
case "added":
|
|
9050
9060
|
console.log(`Production marker: added to ${shell} (guarded by ${productionGate(framework)}).`);
|