@validate.qa/runner 1.0.102 → 1.0.104
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 +7 -2
- package/dist/{chunk-E2VEMBQQ.mjs → chunk-6N4QCCEH.mjs} +2 -2
- package/dist/{chunk-ZZYBRIBT.mjs → chunk-EBWZ7NGU.mjs} +11 -8
- package/dist/{chunk-3JM6XTSZ.mjs → chunk-EKY6CN4Y.mjs} +660 -123
- package/dist/{chunk-DN3O6EYT.mjs → chunk-MEE4DXRX.mjs} +19834 -3064
- package/dist/{chunk-CBVHPYVJ.mjs → chunk-OXDU6ARE.mjs} +86 -7
- package/dist/{chunk-AAUHXGDS.mjs → chunk-PUMRAFJS.mjs} +37 -13
- package/dist/{chunk-4WLP3RWN.mjs → chunk-TGVKABFN.mjs} +45 -3
- package/dist/{chunk-5DFBEL72.mjs → chunk-TS4OUJVT.mjs} +1 -1
- package/dist/{chunk-BXLEPT4M.mjs → chunk-UR7KDOTP.mjs} +32 -8
- package/dist/{chunk-E2RRAE27.mjs → chunk-UZ67UBXH.mjs} +14 -3
- package/dist/{chunk-HJ6FKHMM.mjs → chunk-VWE3SQ2Z.mjs} +1 -1
- package/dist/{chunk-PWFWMVZU.mjs → chunk-XXZZMG6Q.mjs} +15 -3
- package/dist/cli.js +35519 -17101
- package/dist/cli.mjs +1171 -251
- package/dist/credential-mcp-server.js +750 -133
- package/dist/credential-mcp-server.mjs +3 -3
- package/dist/discovery-mcp-server.js +714 -132
- package/dist/discovery-mcp-server.mjs +4 -4
- package/dist/dist-BXHLIG5H.mjs +17 -0
- package/dist/inbox-mcp-server.js +743 -129
- package/dist/inbox-mcp-server.mjs +3 -3
- package/dist/route-seed-worker.js +672 -125
- package/dist/route-seed-worker.mjs +3 -3
- package/dist/run-write-guard-preload.js +714 -126
- package/dist/run-write-guard-preload.mjs +4 -4
- package/dist/run-write-guard.js +714 -126
- package/dist/run-write-guard.mjs +3 -3
- package/dist/tar-extract-child.js +38 -14
- package/dist/tar-extract-child.mjs +2 -2
- package/package.json +3 -2
- package/dist/dist-52M5RDZ6.mjs +0 -17
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dist_exports,
|
|
3
3
|
init_dist
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-EKY6CN4Y.mjs";
|
|
5
5
|
import {
|
|
6
6
|
__commonJS,
|
|
7
7
|
__esm,
|
|
@@ -67467,6 +67467,19 @@ var require_harness_init_script = __commonJS({
|
|
|
67467
67467
|
const finishTransitions = opts.finishTransitions ?? (0, shared_1.parseBoolFlag)(process.env.HARNESS_FINISH_CSS_TRANSITIONS);
|
|
67468
67468
|
const css = finishTransitions ? "*, *::before, *::after { animation-duration: 0s !important; animation-delay: 0s !important; }" : "*, *::before, *::after { transition-duration: 0s !important; animation-duration: 0s !important; transition-delay: 0s !important; animation-delay: 0s !important; }";
|
|
67469
67469
|
return withHarnessSourceUrl(`(() => {
|
|
67470
|
+
// An error the browser throws for the page's own value, passed through one of the wrappers below (a bad
|
|
67471
|
+
// adoptedStyleSheets list, animate timing it refuses), is the app's: stock Playwright reports it as a page error.
|
|
67472
|
+
// Thrown from our wrapper, its top frame would be this script, and the console evidence drops such an error as
|
|
67473
|
+
// the runner's own. Our frames come out of its stack, so the app's call is the top frame, as without the harness.
|
|
67474
|
+
// (Under Error.stackTraceLimit the stack then has one or two frames fewer than stock's; the top frame is right.)
|
|
67475
|
+
const own = ${JSON.stringify(exports.HARNESS_INIT_SOURCE_URL)};
|
|
67476
|
+
const appError = (e) => {
|
|
67477
|
+
try {
|
|
67478
|
+
const stack = e !== null && typeof e === 'object' ? e.stack : undefined;
|
|
67479
|
+
if (typeof stack === 'string' && stack.includes(own)) e.stack = stack.split('\\n').filter((line) => !line.includes(own)).join('\\n');
|
|
67480
|
+
} catch (x) {}
|
|
67481
|
+
return e;
|
|
67482
|
+
};
|
|
67470
67483
|
// Layer 1: CSS transitions and animations - instant, not removed
|
|
67471
67484
|
// (duration 0s preserves final state; 'none' would prevent keyframes from completing)
|
|
67472
67485
|
// The rule goes in a constructed stylesheet on document.adoptedStyleSheets, which adds no node to the DOM:
|
|
@@ -67480,6 +67493,31 @@ var require_harness_init_script = __commonJS({
|
|
|
67480
67493
|
sheet.replaceSync(${JSON.stringify(css)});
|
|
67481
67494
|
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
|
67482
67495
|
adopted = true;
|
|
67496
|
+
// A page that assigns the list without spreading it (document.adoptedStyleSheets = [mine]) would drop the
|
|
67497
|
+
// harness sheet and bring animations back: keep it, last, on every assignment. A bad value (not iterable,
|
|
67498
|
+
// or a list the browser refuses) goes to the browser, which throws its own error as the app's (appError).
|
|
67499
|
+
try {
|
|
67500
|
+
const desc = Object.getOwnPropertyDescriptor(Document.prototype, 'adoptedStyleSheets');
|
|
67501
|
+
if (desc && desc.get && desc.set) {
|
|
67502
|
+
Object.defineProperty(document, 'adoptedStyleSheets', {
|
|
67503
|
+
configurable: true,
|
|
67504
|
+
enumerable: desc.enumerable,
|
|
67505
|
+
get() { return desc.get.call(document); },
|
|
67506
|
+
set(value) {
|
|
67507
|
+
try {
|
|
67508
|
+
if (value === null || (typeof value !== 'object' && typeof value !== 'function') || typeof value[Symbol.iterator] !== 'function') {
|
|
67509
|
+
desc.set.call(document, value);
|
|
67510
|
+
return;
|
|
67511
|
+
}
|
|
67512
|
+
const list = [...value];
|
|
67513
|
+
desc.set.call(document, list.includes(sheet) ? list : [...list, sheet]);
|
|
67514
|
+
} catch (e) {
|
|
67515
|
+
throw appError(e);
|
|
67516
|
+
}
|
|
67517
|
+
},
|
|
67518
|
+
});
|
|
67519
|
+
}
|
|
67520
|
+
} catch (e) {}
|
|
67483
67521
|
}
|
|
67484
67522
|
} catch (e) {}
|
|
67485
67523
|
if (!adopted) try {
|
|
@@ -67556,16 +67594,51 @@ ${finishTransitions ? `
|
|
|
67556
67594
|
` : ""}
|
|
67557
67595
|
// Layer 2: Web Animations API - make instant instead of no-op
|
|
67558
67596
|
// (preserves animation completion callbacks that app code may depend on)
|
|
67597
|
+
// Timing the browser refuses (a negative, NaN or non-'auto' duration, a delay that is not finite) goes to the
|
|
67598
|
+
// browser as the page wrote it: zeroed, it raised no TypeError, so a page error stock Playwright reports was hidden.
|
|
67599
|
+
// Options that are not a plain object (an array, a class instance) keep what they inherit: the browser reads
|
|
67600
|
+
// every timing member through the prototype chain, and an array's inherited fill() is a TypeError. A copy kept
|
|
67601
|
+
// only own properties, so the error was hidden; a proxy overrides only duration and delay.
|
|
67559
67602
|
try {
|
|
67560
67603
|
const origAnimate = Element.prototype.animate;
|
|
67604
|
+
const refused = (options) => {
|
|
67605
|
+
try {
|
|
67606
|
+
if (options === undefined || options === null) return false;
|
|
67607
|
+
if (typeof options !== 'object' && typeof options !== 'function') return !(Number(options) >= 0);
|
|
67608
|
+
const duration = options.duration;
|
|
67609
|
+
if (duration !== undefined && duration !== 'auto' && !(typeof duration === 'number' && duration >= 0)) return true;
|
|
67610
|
+
const delay = options.delay;
|
|
67611
|
+
return delay !== undefined && !Number.isFinite(Number(delay));
|
|
67612
|
+
} catch (e) {
|
|
67613
|
+
return true;
|
|
67614
|
+
}
|
|
67615
|
+
};
|
|
67616
|
+
const instant = (options) => {
|
|
67617
|
+
try {
|
|
67618
|
+
// Plain: its prototype is null or an Object.prototype (of any frame's realm), so it inherits no timing.
|
|
67619
|
+
const proto = Object.getPrototypeOf(options);
|
|
67620
|
+
if (proto !== null && Object.getPrototypeOf(proto) !== null && typeof Proxy === 'function') {
|
|
67621
|
+
// The proxy's target is an empty object, not the options: a target with a read-only, non-configurable
|
|
67622
|
+
// duration or delay (a frozen instance, Object.create with a descriptor) would make returning 0 a proxy
|
|
67623
|
+
// invariant TypeError. Every other member is read from the options, the options as the receiver.
|
|
67624
|
+
return new Proxy({}, { get: (_target, key) => (key === 'duration' || key === 'delay' ? 0 : Reflect.get(options, key)) });
|
|
67625
|
+
}
|
|
67626
|
+
} catch (e) {}
|
|
67627
|
+
return Object.assign({}, options, { duration: 0, delay: 0 });
|
|
67628
|
+
};
|
|
67561
67629
|
if (typeof origAnimate === 'function') {
|
|
67562
67630
|
Element.prototype.animate = function (keyframes, options) {
|
|
67563
|
-
|
|
67564
|
-
options
|
|
67565
|
-
|
|
67566
|
-
|
|
67631
|
+
try {
|
|
67632
|
+
if (refused(options)) return origAnimate.call(this, keyframes, options);
|
|
67633
|
+
if (typeof options === 'object' && options !== null) {
|
|
67634
|
+
options = instant(options);
|
|
67635
|
+
} else {
|
|
67636
|
+
options = 0; // covers number, undefined, and any other type
|
|
67637
|
+
}
|
|
67638
|
+
return origAnimate.call(this, keyframes, options);
|
|
67639
|
+
} catch (e) {
|
|
67640
|
+
throw appError(e);
|
|
67567
67641
|
}
|
|
67568
|
-
return origAnimate.call(this, keyframes, options);
|
|
67569
67642
|
};
|
|
67570
67643
|
}
|
|
67571
67644
|
} catch (e) {}
|
|
@@ -68004,7 +68077,13 @@ await expect(page).not.toHaveURL(/accounts\\.google\\.com/, { timeout: 60_000 })
|
|
|
68004
68077
|
\`\`\`
|
|
68005
68078
|
Never emit code that clicks, fills or asserts anything on accounts.google.com.`;
|
|
68006
68079
|
}
|
|
68007
|
-
function buildGoogleSignInTestGenSection(_opts) {
|
|
68080
|
+
function buildGoogleSignInTestGenSection(_opts, ctx = {}) {
|
|
68081
|
+
if (ctx.appLoginGiven) {
|
|
68082
|
+
return `
|
|
68083
|
+
## SIGN-IN: USE THE TEST ACCOUNT
|
|
68084
|
+
This project gave a test account (the Browser Credentials / \`process.env.TEST_USER_*\` values). Sign in with it on the app's own email (or username) and password form, even when the app also offers Continue with Google. Do not click Continue with Google / Sign in with Google / Sign up with Google to sign in.
|
|
68085
|
+
Use Continue with Google only when this scenario's goal is the Google sign-in itself, or the app has no email/username and password sign-in at all. Then click that control on the app and wait until the browser is back on the app: \`await expect(page).not.toHaveURL(/accounts\\.google\\.com/, { timeout: 60_000 })\` (the runner completes Google's step). Never click, fill or assert anything on accounts.google.com, never type a password or code there.`;
|
|
68086
|
+
}
|
|
68008
68087
|
return `
|
|
68009
68088
|
## GOOGLE SIGN-IN (the runner completes Google's step)
|
|
68010
68089
|
When this scenario uses Continue with Google / Sign in with Google / Sign up with Google:
|
|
@@ -6225,7 +6225,7 @@ var require_repo_acquire = __commonJS({
|
|
|
6225
6225
|
return mod && mod.__esModule ? mod : { "default": mod };
|
|
6226
6226
|
};
|
|
6227
6227
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6228
|
-
exports.ARCHIVE_TOO_LARGE_RE = exports.MAX_ZIP_TOTAL_BYTES = exports.MAX_ZIP_FILE_BYTES = exports.MAX_ZIP_ENTRIES = exports.MAX_ARCHIVE_BYTES = void 0;
|
|
6228
|
+
exports.ARCHIVE_TOO_LARGE_RE = exports.REPO_TARBALL_LIMITS = exports.MAX_ZIP_TOTAL_BYTES = exports.MAX_ZIP_FILE_BYTES = exports.MAX_ZIP_ENTRIES = exports.MAX_ARCHIVE_BYTES = void 0;
|
|
6229
6229
|
exports.acquireRepoForImport = acquireRepoForImport;
|
|
6230
6230
|
exports.downloadArchiveCapped = downloadArchiveCapped;
|
|
6231
6231
|
exports.formatArchiveCap = formatArchiveCap;
|
|
@@ -6251,6 +6251,7 @@ var require_repo_acquire = __commonJS({
|
|
|
6251
6251
|
exports.MAX_ZIP_ENTRIES = 2e4;
|
|
6252
6252
|
exports.MAX_ZIP_FILE_BYTES = 50 * 1024 * 1024;
|
|
6253
6253
|
exports.MAX_ZIP_TOTAL_BYTES = 600 * 1024 * 1024;
|
|
6254
|
+
exports.REPO_TARBALL_LIMITS = { maxEntries: 2e5, maxFileBytes: exports.MAX_ZIP_FILE_BYTES, maxTotalBytes: exports.MAX_ZIP_TOTAL_BYTES, oversizeFiles: "skip" };
|
|
6254
6255
|
function envGithubTokenForImports() {
|
|
6255
6256
|
const optedIn = ["1", "true", "yes", "on"].includes(String(process.env.RUNNER_IMPORT_ENV_GITHUB_TOKEN ?? "").trim().toLowerCase());
|
|
6256
6257
|
if (!optedIn)
|
|
@@ -6294,7 +6295,14 @@ var require_repo_acquire = __commonJS({
|
|
|
6294
6295
|
extractRoot,
|
|
6295
6296
|
/* strip */
|
|
6296
6297
|
1,
|
|
6297
|
-
{
|
|
6298
|
+
{
|
|
6299
|
+
subject: "repository archive",
|
|
6300
|
+
onUnavailable: "in-process",
|
|
6301
|
+
log,
|
|
6302
|
+
signal: spec.signal,
|
|
6303
|
+
tenantKey: spec.projectId,
|
|
6304
|
+
limits: { ...exports.REPO_TARBALL_LIMITS }
|
|
6305
|
+
}
|
|
6298
6306
|
);
|
|
6299
6307
|
await (0, promises_1.rm)(tarPath, { force: true }).catch(noop);
|
|
6300
6308
|
log(`[repo-acquire] extracted github tarball (${archiveBytes} bytes)`);
|
|
@@ -6317,7 +6325,8 @@ var require_repo_acquire = __commonJS({
|
|
|
6317
6325
|
log,
|
|
6318
6326
|
signal: spec.signal,
|
|
6319
6327
|
tenantKey: spec.projectId,
|
|
6320
|
-
hint: "Check it is a valid .tar.gz, or upload it as a .zip."
|
|
6328
|
+
hint: "Check it is a valid .tar.gz, or upload it as a .zip.",
|
|
6329
|
+
limits: { ...exports.REPO_TARBALL_LIMITS }
|
|
6321
6330
|
}
|
|
6322
6331
|
);
|
|
6323
6332
|
} else if (kind === "zip") {
|
|
@@ -6326,7 +6335,8 @@ var require_repo_acquire = __commonJS({
|
|
|
6326
6335
|
log,
|
|
6327
6336
|
signal: spec.signal,
|
|
6328
6337
|
tenantKey: spec.projectId,
|
|
6329
|
-
hint: "Check it is a valid .zip, or upload it as a .tar.gz."
|
|
6338
|
+
hint: "Check it is a valid .zip, or upload it as a .tar.gz.",
|
|
6339
|
+
oversizeFiles: exports.REPO_TARBALL_LIMITS.oversizeFiles
|
|
6330
6340
|
});
|
|
6331
6341
|
} else {
|
|
6332
6342
|
await (0, promises_1.rm)(archivePath, { force: true }).catch(noop);
|
|
@@ -6536,7 +6546,7 @@ var require_repo_acquire = __commonJS({
|
|
|
6536
6546
|
bad = new Error(`Tar entry path contains a NUL byte: ${JSON.stringify(shownPath)}`);
|
|
6537
6547
|
} else if (typeof size !== "number" || !Number.isSafeInteger(size) || size < 0 || size > MAX_OCTAL_TAR_SIZE) {
|
|
6538
6548
|
bad = new Error(`Tar entry has an invalid size field: ${shownPath}`);
|
|
6539
|
-
} else if (size > limits.maxFileBytes) {
|
|
6549
|
+
} else if (size > limits.maxFileBytes && limits.oversizeFiles !== "skip") {
|
|
6540
6550
|
bad = new Error(`Tar entry too large: ${shownPath} (declared ${size} bytes > ${limits.maxFileBytes} cap)`);
|
|
6541
6551
|
} else if ((declaredTotal += size) > limits.maxTotalBytes) {
|
|
6542
6552
|
bad = new Error(`Tar total uncompressed size exceeds ${limits.maxTotalBytes} cap (tar-bomb guard)`);
|
|
@@ -6603,7 +6613,8 @@ var require_repo_acquire = __commonJS({
|
|
|
6603
6613
|
return false;
|
|
6604
6614
|
}
|
|
6605
6615
|
const sz = Number(entry.size ?? 0);
|
|
6606
|
-
|
|
6616
|
+
const oversize = sz > limits.maxFileBytes;
|
|
6617
|
+
if (oversize && limits.oversizeFiles !== "skip") {
|
|
6607
6618
|
capError = new Error(`Tar entry too large: ${p} (declared ${sz} bytes > ${limits.maxFileBytes} cap)`);
|
|
6608
6619
|
return false;
|
|
6609
6620
|
}
|
|
@@ -6612,13 +6623,13 @@ var require_repo_acquire = __commonJS({
|
|
|
6612
6623
|
capError = new Error(`Tar total uncompressed size exceeds ${limits.maxTotalBytes} cap (tar-bomb guard)`);
|
|
6613
6624
|
return false;
|
|
6614
6625
|
}
|
|
6615
|
-
return
|
|
6626
|
+
return !oversize;
|
|
6616
6627
|
}
|
|
6617
6628
|
}));
|
|
6618
6629
|
if (capError)
|
|
6619
6630
|
throw capError;
|
|
6620
6631
|
}
|
|
6621
|
-
async function extractZipSafely(buf, destDir, log = noop) {
|
|
6632
|
+
async function extractZipSafely(buf, destDir, log = noop, options = {}) {
|
|
6622
6633
|
const zip = new adm_zip_1.default(buf);
|
|
6623
6634
|
const entries = zip.getEntries();
|
|
6624
6635
|
if (entries.length > exports.MAX_ZIP_ENTRIES) {
|
|
@@ -6627,6 +6638,7 @@ var require_repo_acquire = __commonJS({
|
|
|
6627
6638
|
const destResolved = (0, node_path_1.resolve)(destDir);
|
|
6628
6639
|
let total = 0;
|
|
6629
6640
|
let files = 0;
|
|
6641
|
+
let skipped = 0;
|
|
6630
6642
|
for (const entry of entries) {
|
|
6631
6643
|
const name = entry.entryName;
|
|
6632
6644
|
if (isUnsafeEntryName(name)) {
|
|
@@ -6641,12 +6653,18 @@ var require_repo_acquire = __commonJS({
|
|
|
6641
6653
|
continue;
|
|
6642
6654
|
}
|
|
6643
6655
|
const declared = entry.header?.size ?? 0;
|
|
6644
|
-
if (declared > exports.MAX_ZIP_FILE_BYTES) {
|
|
6645
|
-
throw new Error(`Zip entry too large: ${name} (declared ${declared} bytes > ${exports.MAX_ZIP_FILE_BYTES} cap)`);
|
|
6646
|
-
}
|
|
6647
6656
|
if (total + declared > exports.MAX_ZIP_TOTAL_BYTES) {
|
|
6648
6657
|
throw new Error(`Zip total uncompressed size exceeds ${exports.MAX_ZIP_TOTAL_BYTES} cap (zip-bomb guard)`);
|
|
6649
6658
|
}
|
|
6659
|
+
if (declared > exports.MAX_ZIP_FILE_BYTES && options.oversizeFiles !== "skip") {
|
|
6660
|
+
throw new Error(`Zip entry too large: ${name} (declared ${declared} bytes > ${exports.MAX_ZIP_FILE_BYTES} cap)`);
|
|
6661
|
+
}
|
|
6662
|
+
if (declared > exports.MAX_ZIP_FILE_BYTES) {
|
|
6663
|
+
total += declared;
|
|
6664
|
+
skipped++;
|
|
6665
|
+
log(`[repo-acquire] zip: skipped ${name} (declared ${declared} bytes > ${exports.MAX_ZIP_FILE_BYTES} per-file cap)`);
|
|
6666
|
+
continue;
|
|
6667
|
+
}
|
|
6650
6668
|
const data = entry.getData();
|
|
6651
6669
|
if (data.length > exports.MAX_ZIP_FILE_BYTES) {
|
|
6652
6670
|
throw new Error(`Zip entry too large: ${name} (${data.length} bytes > ${exports.MAX_ZIP_FILE_BYTES} cap)`);
|
|
@@ -6659,7 +6677,7 @@ var require_repo_acquire = __commonJS({
|
|
|
6659
6677
|
await (0, promises_1.writeFile)(target, data);
|
|
6660
6678
|
files++;
|
|
6661
6679
|
}
|
|
6662
|
-
log(`[repo-acquire] zip extracted: ${files} file(s), ${total} bytes`);
|
|
6680
|
+
log(`[repo-acquire] zip extracted: ${files} file(s), ${total} bytes${skipped ? `, ${skipped} oversize file(s) skipped` : ""}`);
|
|
6663
6681
|
}
|
|
6664
6682
|
async function normalizeRepoRoot(extractRoot) {
|
|
6665
6683
|
let entries;
|
|
@@ -7162,7 +7180,13 @@ var require_tar_isolated = __commonJS({
|
|
|
7162
7180
|
}
|
|
7163
7181
|
async function extractZipIsolated(archivePath, destDir, options) {
|
|
7164
7182
|
try {
|
|
7165
|
-
|
|
7183
|
+
const job = {
|
|
7184
|
+
op: "extract-zip",
|
|
7185
|
+
archivePath: (0, node_path_1.resolve)(archivePath),
|
|
7186
|
+
destDir: (0, node_path_1.resolve)(destDir),
|
|
7187
|
+
...options.oversizeFiles ? { oversizeFiles: options.oversizeFiles } : {}
|
|
7188
|
+
};
|
|
7189
|
+
await runTarJobIsolated(job, options.subject, options);
|
|
7166
7190
|
} catch (err) {
|
|
7167
7191
|
if (err instanceof TarIsolationUnavailableError)
|
|
7168
7192
|
throw refuseUnavailable(err, options.subject, options.log);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
require_survey_network_guard
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UZ67UBXH.mjs";
|
|
4
4
|
import {
|
|
5
5
|
dist_exports,
|
|
6
6
|
init_dist
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-EKY6CN4Y.mjs";
|
|
8
8
|
import {
|
|
9
9
|
__commonJS,
|
|
10
10
|
__require,
|
|
@@ -462,6 +462,13 @@ var require_run_write_guard = __commonJS({
|
|
|
462
462
|
function currentRunSignInSecrets() {
|
|
463
463
|
return policyScope.getStore()?.signInSecrets ?? [];
|
|
464
464
|
}
|
|
465
|
+
function isSocketIoPollingPost(pathname, search) {
|
|
466
|
+
if (pathname !== "/socket.io" && pathname !== "/socket.io/")
|
|
467
|
+
return false;
|
|
468
|
+
const eio = search.getAll("EIO");
|
|
469
|
+
const transport = search.getAll("transport");
|
|
470
|
+
return eio.length === 1 && /^[0-9]{1,2}$/.test(eio[0]) && transport.length === 1 && transport[0] === "polling";
|
|
471
|
+
}
|
|
465
472
|
var READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
466
473
|
var MAX_BODY_CHARS = 262144;
|
|
467
474
|
var MAX_SIGN_IN_LOG_LINES = 20;
|
|
@@ -1296,7 +1303,8 @@ var require_run_write_guard = __commonJS({
|
|
|
1296
1303
|
const method = facts.method.toUpperCase();
|
|
1297
1304
|
const host = url.hostname.toLowerCase().replace(/\.$/, "");
|
|
1298
1305
|
const pathname = url.pathname;
|
|
1299
|
-
|
|
1306
|
+
const queryRead = method === "QUERY" && !(0, survey_network_guard_js_1.queryMethodMayWrite)(url, facts.postData, isGraphqlPath(pathname));
|
|
1307
|
+
if (READ_METHODS.has(method) || queryRead) {
|
|
1300
1308
|
if (method === "GET" && isGraphqlPath(pathname) && (0, survey_network_guard_js_1.graphqlGetMayWrite)(url, facts.postData)) {
|
|
1301
1309
|
return { action: "write", rule: "graphql-get-mutation", record: describe(facts, url, "graphql-get-mutation") };
|
|
1302
1310
|
}
|
|
@@ -1351,6 +1359,8 @@ var require_run_write_guard = __commonJS({
|
|
|
1351
1359
|
if (segs.length > 0 && SEARCH_LAST_SEGMENTS.has(segs[segs.length - 1]))
|
|
1352
1360
|
return { action: "allow", rule: "search-post" };
|
|
1353
1361
|
}
|
|
1362
|
+
if (appSite && method === "POST" && isSocketIoPollingPost(pathname, url.searchParams))
|
|
1363
|
+
return { action: "allow", rule: "realtime-poll" };
|
|
1354
1364
|
if (hostIs(host, PAYMENT_HOSTS))
|
|
1355
1365
|
return { action: "write", rule: "payment", record: describe(facts, url, "payment") };
|
|
1356
1366
|
if (appSite)
|
|
@@ -2155,6 +2165,32 @@ var require_run_write_guard = __commonJS({
|
|
|
2155
2165
|
return false;
|
|
2156
2166
|
}
|
|
2157
2167
|
}
|
|
2168
|
+
function contextStartsOffline(context) {
|
|
2169
|
+
try {
|
|
2170
|
+
return context._options?.offline === true;
|
|
2171
|
+
} catch {
|
|
2172
|
+
return false;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
function trackOffline(context) {
|
|
2176
|
+
const state = { offline: contextStartsOffline(context) };
|
|
2177
|
+
const original = context.setOffline;
|
|
2178
|
+
if (typeof original === "function") {
|
|
2179
|
+
Object.assign(context, {
|
|
2180
|
+
setOffline: async (offline) => {
|
|
2181
|
+
const before = state.offline;
|
|
2182
|
+
state.offline = offline === true;
|
|
2183
|
+
try {
|
|
2184
|
+
await original.call(context, offline);
|
|
2185
|
+
} catch (err) {
|
|
2186
|
+
state.offline = before;
|
|
2187
|
+
throw err;
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
});
|
|
2191
|
+
}
|
|
2192
|
+
return state;
|
|
2193
|
+
}
|
|
2158
2194
|
function hopRequestHeaders(pageHeaders, cookie) {
|
|
2159
2195
|
const out = { accept: "*/*" };
|
|
2160
2196
|
for (const [name, value] of Object.entries(pageHeaders)) {
|
|
@@ -2484,6 +2520,7 @@ var require_run_write_guard = __commonJS({
|
|
|
2484
2520
|
return existing;
|
|
2485
2521
|
const guard = new RunWriteGuard(policy, opts);
|
|
2486
2522
|
countAppWebSocketMessages(context, guard);
|
|
2523
|
+
const emulated = policy.mode === "block" ? trackOffline(context) : { offline: false };
|
|
2487
2524
|
if (policy.mode === "observe") {
|
|
2488
2525
|
context.on("request", (request) => {
|
|
2489
2526
|
try {
|
|
@@ -2516,6 +2553,11 @@ var require_run_write_guard = __commonJS({
|
|
|
2516
2553
|
}
|
|
2517
2554
|
}
|
|
2518
2555
|
if (decision.action === "allow" && !READ_METHODS.has(request.method().toUpperCase()) && checksRedirect(decision.rule, request.url(), guard.policy)) {
|
|
2556
|
+
if (emulated.offline) {
|
|
2557
|
+
await route.fallback().catch(() => {
|
|
2558
|
+
});
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2519
2561
|
await sendCheckingRedirect(route, guard, context);
|
|
2520
2562
|
return;
|
|
2521
2563
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
require_route_manifest
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XXZZMG6Q.mjs";
|
|
4
4
|
import {
|
|
5
5
|
require_survey_network_guard
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UZ67UBXH.mjs";
|
|
7
7
|
import {
|
|
8
8
|
dist_exports,
|
|
9
9
|
init_dist
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-EKY6CN4Y.mjs";
|
|
11
11
|
import {
|
|
12
12
|
__commonJS,
|
|
13
13
|
__toCommonJS
|
|
@@ -159,9 +159,10 @@ var require_exploration_parser = __commonJS({
|
|
|
159
159
|
return false;
|
|
160
160
|
return RECORD_TITLE_WITH_COMPANY.test(name);
|
|
161
161
|
}
|
|
162
|
-
var TYPED_VALUE_CALL_RE = /\.(fill|type|pressSequentially|insertText)\(\s*(['"])(?:\\.|(?!\2)[^\\])*\2(\s*,\s*\{[^{}]*\})?\s*\)/g;
|
|
162
|
+
var TYPED_VALUE_CALL_RE = /\.(fill|type|pressSequentially|insertText)\(\s*(['"`])(?:\\.|(?!\2)[^\\])*\2(\s*,\s*\{[^{}]*\})?\s*\)/g;
|
|
163
|
+
var TWO_ARG_TYPED_VALUE_CALL_RE = /\.(fill|type)\(\s*((?:(['"`])(?:\\.|(?!\3)[^\\])*\3|[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*))\s*,\s*(['"`])(?:\\.|(?!\4)[^\\])*\4(\s*,\s*\{[^{}]*\})?\s*\)/g;
|
|
163
164
|
function maskTypedValues(codeLine, mask) {
|
|
164
|
-
return codeLine.replace(TYPED_VALUE_CALL_RE, (_all, method, _quote, options) => `.${method}(${mask}${options ?? ""})`);
|
|
165
|
+
return codeLine.replace(TWO_ARG_TYPED_VALUE_CALL_RE, (_all, method, selector, _selQuote, _quote, options) => `.${method}(${selector}, ${mask}${options ?? ""})`).replace(TYPED_VALUE_CALL_RE, (_all, method, _quote, options) => `.${method}(${mask}${options ?? ""})`);
|
|
165
166
|
}
|
|
166
167
|
function sanitizePromptCodeLine(codeLine) {
|
|
167
168
|
let sanitized = codeLine;
|
|
@@ -1903,8 +1904,9 @@ var require_repo_feature_verification = __commonJS({
|
|
|
1903
1904
|
"the",
|
|
1904
1905
|
"for"
|
|
1905
1906
|
]);
|
|
1907
|
+
var UNSPACED_SCRIPT = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
|
|
1906
1908
|
function rootCardTerms(featureName) {
|
|
1907
|
-
const words = String(featureName ?? "").toLowerCase().split(/[
|
|
1909
|
+
const words = String(featureName ?? "").toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((w) => [...w].length >= (UNSPACED_SCRIPT.test(w) ? 2 : 4) && !ROOT_GENERIC_WORDS.has(w));
|
|
1908
1910
|
return [...new Set(words.map((w) => w.length > 4 && w.endsWith("s") ? w.slice(0, -1) : w))];
|
|
1909
1911
|
}
|
|
1910
1912
|
function textShowsAnyTerm(text, terms) {
|
|
@@ -1958,7 +1960,7 @@ var require_repo_feature_verification = __commonJS({
|
|
|
1958
1960
|
return null;
|
|
1959
1961
|
}
|
|
1960
1962
|
function normalizeControlText(text) {
|
|
1961
|
-
return text.toLowerCase().replace(/[
|
|
1963
|
+
return text.normalize("NFKD").replace(/(\p{Script=Latin})\p{M}+/gu, "$1").normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}\p{M}]+/gu, " ").trim();
|
|
1962
1964
|
}
|
|
1963
1965
|
var SESSION_END_LABEL = /^(?:log|sign) ?(?:out|off)$/;
|
|
1964
1966
|
var SESSION_END_SPELLINGS = ["log out", "logout", "log off", "logoff", "sign out", "signout", "sign off", "signoff"];
|
|
@@ -1971,13 +1973,35 @@ var require_repo_feature_verification = __commonJS({
|
|
|
1971
1973
|
return [];
|
|
1972
1974
|
return SESSION_END_LABEL.test(wanted) ? SESSION_END_SPELLINGS : [wanted];
|
|
1973
1975
|
}
|
|
1976
|
+
var SHORT_LABEL_CHARS = 2;
|
|
1977
|
+
var WORD_SEGMENTER = new Intl.Segmenter(void 0, { granularity: "word" });
|
|
1978
|
+
function wholeWordsSpell(text, wanted) {
|
|
1979
|
+
const words = [];
|
|
1980
|
+
for (const s of WORD_SEGMENTER.segment(text))
|
|
1981
|
+
if (s.isWordLike)
|
|
1982
|
+
words.push(s.segment);
|
|
1983
|
+
for (let i = 0; i < words.length; i++) {
|
|
1984
|
+
let run = "";
|
|
1985
|
+
for (let j = i; j < words.length && run.length < wanted.length; j++) {
|
|
1986
|
+
run += words[j];
|
|
1987
|
+
if (run === wanted)
|
|
1988
|
+
return true;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
return false;
|
|
1992
|
+
}
|
|
1974
1993
|
function textNamesControl(text, label) {
|
|
1975
1994
|
const spellings = controlSpellings(label);
|
|
1976
1995
|
if (spellings.length === 0)
|
|
1977
1996
|
return false;
|
|
1978
1997
|
const haystack = normalizeControlText(text);
|
|
1979
1998
|
const squeezed = haystack.replace(/ /g, "");
|
|
1980
|
-
return spellings.some((w) =>
|
|
1999
|
+
return spellings.some((w) => {
|
|
2000
|
+
const bare = w.replace(/ /g, "");
|
|
2001
|
+
if (!haystack.includes(w) && !squeezed.includes(bare))
|
|
2002
|
+
return false;
|
|
2003
|
+
return [...bare].length > SHORT_LABEL_CHARS || wholeWordsSpell(haystack, bare);
|
|
2004
|
+
});
|
|
1981
2005
|
}
|
|
1982
2006
|
function pageShowsControl(page, label) {
|
|
1983
2007
|
const text = capturedPageText(page);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dist_exports,
|
|
3
3
|
init_dist
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-EKY6CN4Y.mjs";
|
|
5
5
|
import {
|
|
6
6
|
__commonJS,
|
|
7
7
|
__require,
|
|
@@ -27,6 +27,7 @@ var require_survey_network_guard = __commonJS({
|
|
|
27
27
|
exports.graphqlUrlCarriesQuery = graphqlUrlCarriesQuery;
|
|
28
28
|
exports.graphqlUrlQueriesAreReads = graphqlUrlQueriesAreReads;
|
|
29
29
|
exports.graphqlGetMayWrite = graphqlGetMayWrite;
|
|
30
|
+
exports.queryMethodMayWrite = queryMethodMayWrite;
|
|
30
31
|
exports.writeShapedGet = writeShapedGet;
|
|
31
32
|
exports.decideSurveyRequest = decideSurveyRequest;
|
|
32
33
|
exports.installSurveyNetworkGuard = installSurveyNetworkGuard;
|
|
@@ -591,6 +592,12 @@ var require_survey_network_guard = __commonJS({
|
|
|
591
592
|
const docs = url.searchParams.getAll("query");
|
|
592
593
|
return docs.length > 0 && (typeof body === "string" && body.length > 0 || docs.some((doc) => !graphqlDocumentIsQueryOnly(doc)));
|
|
593
594
|
}
|
|
595
|
+
function queryMethodMayWrite(url, body, graphqlEndpoint) {
|
|
596
|
+
if (graphqlEndpoint)
|
|
597
|
+
return true;
|
|
598
|
+
const operation = /\b(?:mutation|subscription)\b/i;
|
|
599
|
+
return typeof body === "string" && operation.test(body) || [...url.searchParams.values()].some((v) => operation.test(v));
|
|
600
|
+
}
|
|
594
601
|
function isGraphqlGetWrite(url, s, body) {
|
|
595
602
|
return isGraphqlEndpoint(url, s) && graphqlGetMayWrite(url, body);
|
|
596
603
|
}
|
|
@@ -618,6 +625,7 @@ var require_survey_network_guard = __commonJS({
|
|
|
618
625
|
"log-out"
|
|
619
626
|
]);
|
|
620
627
|
exports.AMBIGUOUS_WRITE_VERBS = /* @__PURE__ */ new Set(["archive", "confirm", "disable"]);
|
|
628
|
+
var LABEL_PAGE_NOUNS = /* @__PURE__ */ new Set(["archive"]);
|
|
621
629
|
var RESET_PAGE_WORDS = /* @__PURE__ */ new Set(["reset-password", "password-reset", "reset_password", "password_reset", "resetpassword", "forgot-password"]);
|
|
622
630
|
var isTokenLikeSegment = (seg) => exports.ID_SEGMENT_RE.test(seg) || /^[A-Za-z0-9_-]{20,}$/.test(seg);
|
|
623
631
|
var WRITE_SHAPED_STATIC_EXTENSIONS = [
|
|
@@ -763,6 +771,8 @@ var require_survey_network_guard = __commonJS({
|
|
|
763
771
|
if (oneTimeToken)
|
|
764
772
|
return { reason: "one-time-token" };
|
|
765
773
|
const verb = opts.triggerLabel ? labelVerb(opts.triggerLabel) : void 0;
|
|
774
|
+
if (verb && LABEL_PAGE_NOUNS.has(verb) && last === verb && params.length === 0)
|
|
775
|
+
return null;
|
|
766
776
|
return verb && !opts.ignoreVerbs?.has(verb) ? { reason: "label", verb } : null;
|
|
767
777
|
}
|
|
768
778
|
function writeShapedText(w) {
|
|
@@ -797,9 +807,10 @@ var require_survey_network_guard = __commonJS({
|
|
|
797
807
|
return { action: "allow", rule: readMethod ? "read" : "non-http" };
|
|
798
808
|
const appSite = isAppSite(url.hostname, s.appBaseUrl);
|
|
799
809
|
const graphqlGet = method === "GET" && isGraphqlGetWrite(url, s, req.postData);
|
|
810
|
+
const queryRead = method === "QUERY" && !queryMethodMayWrite(url, req.postData, isGraphqlEndpoint(url, s));
|
|
800
811
|
const inActionWindow = s.lastActionAt > 0 && s.now - s.lastActionAt <= ACTION_NAVIGATION_WINDOW_MS;
|
|
801
812
|
const typedLoad = s.agentLoad === true && req.resourceType === "document";
|
|
802
|
-
const writeShaped = method === "GET" && appSite && !graphqlGet && (inActionWindow || typedLoad) ? writeShapedGet(req.url, {
|
|
813
|
+
const writeShaped = (method === "GET" || queryRead) && appSite && !graphqlGet && (inActionWindow || typedLoad) ? writeShapedGet(req.url, {
|
|
803
814
|
appSites: appSitesOf(s.appBaseUrl),
|
|
804
815
|
resourceType: req.resourceType ?? "",
|
|
805
816
|
// A label says what a click opens: it only judges a page load, never the app's own fetches, and never a typed URL.
|
|
@@ -807,7 +818,7 @@ var require_survey_network_guard = __commonJS({
|
|
|
807
818
|
}) : null;
|
|
808
819
|
if (writeShaped)
|
|
809
820
|
return decideWriteShapedGet(req, s, writeShaped);
|
|
810
|
-
if (readMethod && !graphqlGet)
|
|
821
|
+
if ((readMethod || queryRead) && !graphqlGet)
|
|
811
822
|
return { action: "allow", rule: "read" };
|
|
812
823
|
if (!appSite && ((0, shared_1.isAuthProviderHost)(url.hostname) || isTelemetryHost(url.hostname)))
|
|
813
824
|
return { action: "allow", rule: "third-party" };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dist_exports,
|
|
3
3
|
init_dist
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-EKY6CN4Y.mjs";
|
|
5
5
|
import {
|
|
6
6
|
__commonJS,
|
|
7
7
|
__require,
|
|
@@ -133,6 +133,7 @@ var require_repo_brief = __commonJS({
|
|
|
133
133
|
exports.briefSectionForFeature = briefSectionForFeature;
|
|
134
134
|
exports.buildBriefRouteAllowlist = buildBriefRouteAllowlist;
|
|
135
135
|
exports.buildBriefDoNotTestRoutes = buildBriefDoNotTestRoutes;
|
|
136
|
+
exports.briefJobRoute = briefJobRoute;
|
|
136
137
|
exports.unopenedBriefJobs = unopenedBriefJobs;
|
|
137
138
|
var shared_1 = (init_dist(), __toCommonJS(dist_exports));
|
|
138
139
|
var model_json_document_js_1 = require_model_json_document();
|
|
@@ -391,14 +392,25 @@ ${scenarios}` : ""
|
|
|
391
392
|
}
|
|
392
393
|
return [...routes];
|
|
393
394
|
}
|
|
394
|
-
|
|
395
|
+
var ENTRY_ROUTE_RE = /(?:^|[\s(])((?:\/|#\/)[^\s?,;()]*)/;
|
|
396
|
+
function briefJobRoute(job) {
|
|
397
|
+
const match = ENTRY_ROUTE_RE.exec(job.entry.trim());
|
|
398
|
+
if (!match)
|
|
399
|
+
return null;
|
|
400
|
+
const route = match[1].replace(/[.:]+$/, "");
|
|
401
|
+
return route === "#/" ? null : route || null;
|
|
402
|
+
}
|
|
403
|
+
function unopenedBriefJobs(brief, coveredText, isOpenedRoute) {
|
|
395
404
|
const haystack = coveredText.toLowerCase();
|
|
396
405
|
return brief.jobs.filter((job) => {
|
|
397
406
|
if (job.doNotTest)
|
|
398
407
|
return false;
|
|
399
408
|
const name = job.name.toLowerCase();
|
|
400
409
|
const entry = job.entry.toLowerCase();
|
|
401
|
-
|
|
410
|
+
if (haystack.includes(name) || haystack.includes(entry))
|
|
411
|
+
return false;
|
|
412
|
+
const route = isOpenedRoute ? briefJobRoute(job) : null;
|
|
413
|
+
return !(route && isOpenedRoute?.(route));
|
|
402
414
|
});
|
|
403
415
|
}
|
|
404
416
|
exports.REPO_BRIEF_PROMPT = [
|