webstore-lint 1.0.31 → 1.0.32

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/README.md CHANGED
@@ -347,6 +347,10 @@ What it reports:
347
347
  It checks *reachability*. It does not judge whether your policy is adequate,
348
348
  which is the thing a reviewer actually reads it for.
349
349
 
350
+ The request has a ten-second deadline, including downloading the page body.
351
+ A timeout or interrupted download produces a network warning, with no verdict
352
+ on the unread page's content.
353
+
350
354
  ## What it cannot do
351
355
 
352
356
  It reads your package. It cannot see your store listing, your screenshots or your
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webstore-lint",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "description": "Check an unpacked Chrome extension against the Chrome Web Store program policies before you submit it. Every finding cites Google's own notification ID and verbatim policy text. Covers the 1 August 2026 policy updates.",
5
5
  "keywords": [
6
6
  "chrome-extension",
@@ -38,7 +38,7 @@
38
38
  "node": ">=18"
39
39
  },
40
40
  "scripts": {
41
- "test": "node test/lint.test.mjs && node test/action.test.mjs && node --test test/permissions-output.test.mjs test/cli-modes.test.mjs"
41
+ "test": "node test/lint.test.mjs && node test/action.test.mjs && node --test test/permissions-output.test.mjs test/cli-modes.test.mjs test/privacy-timeout.test.mjs"
42
42
  },
43
43
  "repository": {
44
44
  "type": "git",
package/src/privacy.mjs CHANGED
@@ -118,6 +118,7 @@ export async function checkPolicyUrl(url, { timeoutMs = 10000, fetchImpl = fetch
118
118
  const ctl = new AbortController();
119
119
  const timer = setTimeout(() => ctl.abort(), timeoutMs);
120
120
  let res;
121
+ let body;
121
122
  try {
122
123
  res = await fetchImpl(u.href, {
123
124
  signal: ctl.signal,
@@ -129,24 +130,30 @@ export async function checkPolicyUrl(url, { timeoutMs = 10000, fetchImpl = fetch
129
130
  "user-agent": "webstore-lint (privacy policy reachability check)",
130
131
  },
131
132
  });
133
+ // fetch resolves at headers. Keep the deadline active until the body is
134
+ // complete too, or a stalled page can hang the CLI and Action indefinitely.
135
+ if (res.ok) body = await res.text();
136
+ else ctl.abort(); // The status is enough evidence; do not download its body.
132
137
  } catch (e) {
133
- clearTimeout(timer);
134
138
  // COULD NOT LOOK IS NOT THE SAME FACT AS NOT THERE. A DNS failure on the
135
139
  // developer's own machine, an offline CI runner or a proxy is not evidence
136
140
  // about the URL, so this warns and says which it is rather than failing.
137
- const aborted = e.name === "AbortError";
141
+ const aborted = ctl.signal.aborted || e.name === "AbortError";
138
142
  return [...findings, f(
139
143
  "warn",
140
144
  aborted
141
- ? `The privacy policy URL did not answer within ${timeoutMs / 1000}s`
142
- : `The privacy policy URL could not be reached from here: ${e.message}`,
145
+ ? `The privacy policy URL did not finish loading within ${timeoutMs / 1000}s`
146
+ : res
147
+ ? `The privacy policy response could not be read from here: ${e.message}`
148
+ : `The privacy policy URL could not be reached from here: ${e.message}`,
143
149
  "This is not the same as the address being broken. It may be your network, a proxy, or an offline " +
144
150
  "runner. Open it yourself in a private browser window before you trust either answer. " +
145
151
  "A reviewer will be doing exactly that.",
146
152
  [{ file: "--privacy-policy", text: url }],
147
153
  )];
154
+ } finally {
155
+ clearTimeout(timer);
148
156
  }
149
- clearTimeout(timer);
150
157
 
151
158
  const finalUrl = res.url || u.href;
152
159
  const redirected = finalUrl.replace(/\/$/, "") !== u.href.replace(/\/$/, "");
@@ -163,13 +170,6 @@ export async function checkPolicyUrl(url, { timeoutMs = 10000, fetchImpl = fetch
163
170
  )];
164
171
  }
165
172
 
166
- let body = "";
167
- try {
168
- body = await res.text();
169
- } catch {
170
- body = "";
171
- }
172
-
173
173
  // Strip tags and scripts before counting words, or a single-page app's bundle
174
174
  // reads as a wall of text that mentions everything.
175
175
  const text = body