tewip-ai 0.70.0 → 0.70.1
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/dist/engine/secure/web.js +44 -2
- package/package.json +1 -1
|
@@ -169,12 +169,54 @@ export async function scanWeb(url, { timeoutMs = 15_000, probeExposed = true, pa
|
|
|
169
169
|
return { findings, requests, note: null };
|
|
170
170
|
}
|
|
171
171
|
/** Localhost, a private address, or a name only this machine resolves. */
|
|
172
|
+
/** The four octets of a dotted IPv4 address, or null for anything else - including a name. */
|
|
173
|
+
function ipv4(host) {
|
|
174
|
+
const parts = host.split('.');
|
|
175
|
+
if (parts.length !== 4)
|
|
176
|
+
return null;
|
|
177
|
+
const numbers = parts.map((p) => (/^\d{1,3}$/.test(p) ? Number(p) : -1));
|
|
178
|
+
return numbers.every((n) => n >= 0 && n <= 255) ? numbers : null;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Is this address the person's own machine or network?
|
|
182
|
+
*
|
|
183
|
+
* **This is a consent control, not a convenience.** `secure --url` refuses a non-local address
|
|
184
|
+
* without `--i-am-authorised`, because scanning a system you do not run is not Tewip's decision to
|
|
185
|
+
* make - so anything this returns `true` for is scanned with no questions asked.
|
|
186
|
+
*
|
|
187
|
+
* It was a prefix match, and five hostnames walked straight past it: `localhost.evil.example`,
|
|
188
|
+
* `localhostile.example`, `10.evil.example`, `127.0.0.1.evil.example` and `172.16.evil.example` were
|
|
189
|
+
* all "this machine", because each *starts with* something local-looking. `localhost.<anything>` is
|
|
190
|
+
* one of the oldest allowlist bypasses there is, and a careless paste reaches it as easily as a
|
|
191
|
+
* malicious one.
|
|
192
|
+
*
|
|
193
|
+
* So a name is local only if it **is** `localhost` or ends in a reserved local suffix, and an address
|
|
194
|
+
* is local only if its octets really fall in a private range. Link-local 169.254/16 is deliberately
|
|
195
|
+
* **not** local: `169.254.169.254` is the cloud metadata endpoint, and asking before touching it is
|
|
196
|
+
* exactly the point of this function.
|
|
197
|
+
*/
|
|
172
198
|
export function isLocal(url) {
|
|
199
|
+
let host;
|
|
173
200
|
try {
|
|
174
|
-
|
|
175
|
-
return /^(?:localhost|127\.|0\.0\.0\.0|\[::1\]|::1|10\.|192\.168\.|172\.(?:1[6-9]|2\d|3[01])\.)/.test(host) || host.endsWith('.local') || host.endsWith('.localhost');
|
|
201
|
+
host = new URL(url).hostname.toLowerCase();
|
|
176
202
|
}
|
|
177
203
|
catch {
|
|
178
204
|
return false;
|
|
179
205
|
}
|
|
206
|
+
// `new URL('http://[::1]').hostname` is "[::1]" in Node, so both spellings are accepted.
|
|
207
|
+
if (host === 'localhost' || host === '::1' || host === '[::1]')
|
|
208
|
+
return true;
|
|
209
|
+
if (host.endsWith('.localhost') || host.endsWith('.local'))
|
|
210
|
+
return true;
|
|
211
|
+
const octets = ipv4(host);
|
|
212
|
+
if (!octets)
|
|
213
|
+
return false;
|
|
214
|
+
const [a, b] = octets;
|
|
215
|
+
if (a === 0 || a === 127)
|
|
216
|
+
return true; // 0.0.0.0 and the whole loopback range
|
|
217
|
+
if (a === 10)
|
|
218
|
+
return true;
|
|
219
|
+
if (a === 192 && b === 168)
|
|
220
|
+
return true;
|
|
221
|
+
return a === 172 && b >= 16 && b <= 31;
|
|
180
222
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tewip-ai",
|
|
3
|
-
"version": "0.70.
|
|
3
|
+
"version": "0.70.1",
|
|
4
4
|
"mcpName": "io.github.hmamut39/tewip",
|
|
5
5
|
"description": "Tewip finds why each failing test failed - real bug, flaky test, environment, test data or changed selector - for any stack, from the command line, CI, or your editor.",
|
|
6
6
|
"keywords": [
|