@validate.qa/runner 1.0.101 → 1.0.102
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 +3 -0
- package/dist/{chunk-IGXGSDE3.mjs → chunk-3JM6XTSZ.mjs} +1298 -384
- package/dist/{chunk-BBTK6OX6.mjs → chunk-4WLP3RWN.mjs} +383 -11
- package/dist/{chunk-XZUUBMP3.mjs → chunk-5DFBEL72.mjs} +1 -1
- package/dist/{chunk-M3VZGHZQ.mjs → chunk-BXLEPT4M.mjs} +20 -10
- package/dist/{chunk-FQUH2PU2.mjs → chunk-CBVHPYVJ.mjs} +62 -2
- package/dist/{chunk-4SZC5ZX3.mjs → chunk-DN3O6EYT.mjs} +19461 -4077
- package/dist/{chunk-SN3PN4G4.mjs → chunk-E2RRAE27.mjs} +6 -2
- package/dist/{chunk-2OKZY3WN.mjs → chunk-E2VEMBQQ.mjs} +2 -2
- package/dist/{chunk-CYWHPXNZ.mjs → chunk-HJ6FKHMM.mjs} +1 -1
- package/dist/{chunk-CO5KHIVX.mjs → chunk-PWFWMVZU.mjs} +1 -1
- package/dist/{chunk-TPU42VH2.mjs → chunk-ZZYBRIBT.mjs} +10 -5
- package/dist/cli.js +135916 -118926
- package/dist/cli.mjs +349 -101
- package/dist/credential-mcp-server.js +1327 -348
- package/dist/credential-mcp-server.mjs +3 -3
- package/dist/discovery-mcp-server.js +1328 -400
- package/dist/discovery-mcp-server.mjs +4 -4
- package/dist/dist-52M5RDZ6.mjs +17 -0
- package/dist/inbox-mcp-server.js +1579 -605
- package/dist/inbox-mcp-server.mjs +3 -3
- package/dist/route-seed-worker.js +1244 -330
- package/dist/route-seed-worker.mjs +4 -4
- package/dist/run-write-guard-preload.js +1622 -332
- package/dist/run-write-guard-preload.mjs +4 -4
- package/dist/run-write-guard.js +1622 -332
- package/dist/run-write-guard.mjs +3 -3
- package/dist/spec-driver.js +1 -1
- package/dist/spec-driver.mjs +1 -1
- package/package.json +3 -2
- package/dist/dist-VTPBY2C4.mjs +0 -17
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
require_survey_network_guard
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-E2RRAE27.mjs";
|
|
4
4
|
import {
|
|
5
5
|
dist_exports,
|
|
6
6
|
init_dist
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3JM6XTSZ.mjs";
|
|
8
8
|
import {
|
|
9
9
|
__commonJS,
|
|
10
10
|
__require,
|
|
@@ -348,7 +348,7 @@ var require_run_write_guard = __commonJS({
|
|
|
348
348
|
"../runner-core/dist/services/run-write-guard.js"(exports) {
|
|
349
349
|
"use strict";
|
|
350
350
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
351
|
-
exports.RUN_WRITE_RELAYED_LINE = exports.WRITE_GUARD_UNAVAILABLE_ERROR_PREFIX = exports.WRITE_HELD_ERROR_PREFIX = exports.RUN_WRITE_UNAVAILABLE_LINE = exports.RUN_WRITE_SAW_LINE = exports.RUN_WRITE_HELD_LINE = exports.RUN_WRITE_GUARD_LINE_PREFIX = exports.RUN_WRITE_SIGNIN_VARS_ENV = exports.RUN_WRITE_POLICY_ENV = exports.NO_KEEPALIVE_INIT_SCRIPT = exports.RUN_WRITE_LEAVING_EVENT = exports.RUN_WRITE_RELAY_KEY = exports.RUN_WRITE_DROPPED_BINDING = exports.RUN_WRITE_BLOCK_CONTEXT_OPTIONS = exports.WEBSOCKET_NOT_GUARDED = exports.RunWriteGuard = exports.BROKER_TOKEN_TTL_MS = exports.MAX_RUN_WRITE_RECORDS = exports.SHARED_SUFFIX_PATTERN_REASON = exports.FIREBASE_AUTH_HANDLER_PATH = exports.SIGN_IN_SECRET_VARIABLE = exports.MAX_ALLOW_PATTERN_CHARS = exports.MAX_ALLOW_PATTERNS = exports.DEFAULT_RECORD_ONLY_RULES = exports.RECORD_ONLY_CAPABLE_RULES = exports.RUN_WRITE_POLICY_VERSION = void 0;
|
|
351
|
+
exports.RUN_WRITE_RELAYED_LINE = exports.WRITE_GUARD_UNAVAILABLE_ERROR_PREFIX = exports.WRITE_HELD_ERROR_PREFIX = exports.RUN_WRITE_UNAVAILABLE_LINE = exports.RUN_WRITE_SAW_LINE = exports.RUN_WRITE_HELD_LINE = exports.RUN_WRITE_GUARD_LINE_PREFIX = exports.RUN_WRITE_SIGNIN_VARS_ENV = exports.RUN_WRITE_POLICY_ENV = exports.NO_KEEPALIVE_INIT_SCRIPT = exports.RUN_WRITE_LEAVING_EVENT = exports.RUN_WRITE_RELAY_KEY = exports.RUN_WRITE_DROPPED_BINDING = exports.RUN_WRITE_BLOCK_CONTEXT_OPTIONS = exports.WEBSOCKET_NOT_GUARDED = exports.RunWriteGuard = exports.BROKER_TOKEN_TTL_MS = exports.MAX_RUN_WRITE_RECORDS = exports.SHARED_SUFFIX_PATTERN_REASON = exports.FIREBASE_AUTH_HANDLER_PATH = exports.SIGN_IN_SECRET_VARIABLE = exports.MAX_ALLOW_PATTERN_CHARS = exports.MAX_ALLOW_PATTERNS = exports.MAX_IDP_HOSTS = exports.DEFAULT_RECORD_ONLY_RULES = exports.RECORD_ONLY_CAPABLE_RULES = exports.RUN_WRITE_POLICY_VERSION = void 0;
|
|
352
352
|
exports.parseRunWritePolicy = parseRunWritePolicy;
|
|
353
353
|
exports.signInSecretsOf = signInSecretsOf;
|
|
354
354
|
exports.withRunWritePolicyScope = withRunWritePolicyScope;
|
|
@@ -357,6 +357,8 @@ var require_run_write_guard = __commonJS({
|
|
|
357
357
|
exports.bodyHasSignInSecret = bodyHasSignInSecret;
|
|
358
358
|
exports.compileAllowPattern = compileAllowPattern;
|
|
359
359
|
exports.allowPatternProblem = allowPatternProblem;
|
|
360
|
+
exports.normaliseIdpHost = normaliseIdpHost;
|
|
361
|
+
exports.idpHostProblem = idpHostProblem;
|
|
360
362
|
exports.runWritePatternNotes = runWritePatternNotes;
|
|
361
363
|
exports.decideRunWrite = decideRunWrite;
|
|
362
364
|
exports.isGoogleBrokerToken = isGoogleBrokerToken;
|
|
@@ -389,6 +391,8 @@ var require_run_write_guard = __commonJS({
|
|
|
389
391
|
exports.RECORD_ONLY_CAPABLE_RULES = ["write-shaped-get"];
|
|
390
392
|
exports.DEFAULT_RECORD_ONLY_RULES = ["write-shaped-get"];
|
|
391
393
|
var MAX_APP_SITES = 16;
|
|
394
|
+
exports.MAX_IDP_HOSTS = 4;
|
|
395
|
+
var MAX_IDP_HINTS = 4;
|
|
392
396
|
exports.MAX_ALLOW_PATTERNS = 50;
|
|
393
397
|
exports.MAX_ALLOW_PATTERN_CHARS = 200;
|
|
394
398
|
var MAX_REASON_CHARS = 300;
|
|
@@ -416,6 +420,11 @@ var require_run_write_guard = __commonJS({
|
|
|
416
420
|
const e = x;
|
|
417
421
|
return e && typeof e.pattern === "string" && typeof e.reason === "string" ? [{ pattern: e.pattern.slice(0, exports.MAX_ALLOW_PATTERN_CHARS), reason: e.reason.slice(0, MAX_REASON_CHARS) }] : [];
|
|
418
422
|
}) : [];
|
|
423
|
+
const idpHosts = Array.isArray(v.idpHosts) ? [...new Set(v.idpHosts.filter((h) => typeof h === "string" && normaliseIdpHost(h) === h && idpHostProblem(h) === null))].slice(0, exports.MAX_IDP_HOSTS) : [];
|
|
424
|
+
const ignoredIdpHosts = Array.isArray(v.ignoredIdpHosts) ? v.ignoredIdpHosts.slice(0, exports.MAX_ALLOW_PATTERNS).flatMap((x) => {
|
|
425
|
+
const e = x;
|
|
426
|
+
return e && typeof e.pattern === "string" && typeof e.reason === "string" ? [{ pattern: e.pattern.slice(0, exports.MAX_ALLOW_PATTERN_CHARS), reason: e.reason.slice(0, MAX_REASON_CHARS) }] : [];
|
|
427
|
+
}) : [];
|
|
419
428
|
const recordOnlyRules = Array.isArray(v.recordOnlyRules) ? exports.RECORD_ONLY_CAPABLE_RULES.filter((rule) => v.recordOnlyRules.includes(rule)) : [...exports.DEFAULT_RECORD_ONLY_RULES];
|
|
420
429
|
if (typeof v.reason !== "string")
|
|
421
430
|
return null;
|
|
@@ -428,6 +437,9 @@ var require_run_write_guard = __commonJS({
|
|
|
428
437
|
appSites: appSites.map((s) => s.toLowerCase()),
|
|
429
438
|
allowPatterns,
|
|
430
439
|
ignoredAllowPatterns,
|
|
440
|
+
// Only when set: a policy without them parses exactly as before.
|
|
441
|
+
...idpHosts.length > 0 ? { idpHosts } : {},
|
|
442
|
+
...ignoredIdpHosts.length > 0 ? { ignoredIdpHosts } : {},
|
|
431
443
|
recordOnlyRules,
|
|
432
444
|
reason: v.reason.slice(0, MAX_REASON_CHARS),
|
|
433
445
|
...v.pwAgentsGuard === "off" ? { pwAgentsGuard: "off" } : {}
|
|
@@ -629,8 +641,153 @@ var require_run_write_guard = __commonJS({
|
|
|
629
641
|
return named.size + Math.min(bare.size, 2) >= 2;
|
|
630
642
|
}
|
|
631
643
|
var GRAPHQL_SIGN_IN_NAME = /^(?:log_?in|sign_?in|authenticate|login\w{0,40}|signin\w{0,40})$/i;
|
|
644
|
+
var GRAPHQL_SIGN_IN_VERBS = ["login", "signin", "authenticate"];
|
|
645
|
+
var GRAPHQL_SIGN_IN_PHRASE_WORDS = /* @__PURE__ */ new Set(["with", "via", "by", "using"]);
|
|
646
|
+
var GRAPHQL_SIGN_IN_TAIL_WORDS = /* @__PURE__ */ new Set([
|
|
647
|
+
"user",
|
|
648
|
+
"mutation",
|
|
649
|
+
"mfa",
|
|
650
|
+
"otp",
|
|
651
|
+
"totp",
|
|
652
|
+
"sso",
|
|
653
|
+
"anonymous",
|
|
654
|
+
"anonymously",
|
|
655
|
+
"email",
|
|
656
|
+
"password",
|
|
657
|
+
"phone",
|
|
658
|
+
"number",
|
|
659
|
+
"code",
|
|
660
|
+
"token",
|
|
661
|
+
"refresh",
|
|
662
|
+
"custom",
|
|
663
|
+
"api",
|
|
664
|
+
"key",
|
|
665
|
+
"magic",
|
|
666
|
+
"credentials",
|
|
667
|
+
"username",
|
|
668
|
+
"credential",
|
|
669
|
+
"sms",
|
|
670
|
+
"pin",
|
|
671
|
+
"jwt",
|
|
672
|
+
"mobile",
|
|
673
|
+
"session",
|
|
674
|
+
"ticket",
|
|
675
|
+
"qr",
|
|
676
|
+
"biometric",
|
|
677
|
+
"biometrics",
|
|
678
|
+
"wallet",
|
|
679
|
+
"identifier",
|
|
680
|
+
"address",
|
|
681
|
+
"local",
|
|
682
|
+
"login",
|
|
683
|
+
"userid",
|
|
684
|
+
"passkey",
|
|
685
|
+
"webauthn",
|
|
686
|
+
"idp",
|
|
687
|
+
"oauth",
|
|
688
|
+
"oidc",
|
|
689
|
+
"saml",
|
|
690
|
+
"ldap",
|
|
691
|
+
"google",
|
|
692
|
+
"apple",
|
|
693
|
+
"github",
|
|
694
|
+
"gitlab",
|
|
695
|
+
"facebook",
|
|
696
|
+
"microsoft",
|
|
697
|
+
"azure",
|
|
698
|
+
"twitter",
|
|
699
|
+
"linkedin",
|
|
700
|
+
"discord",
|
|
701
|
+
"slack",
|
|
702
|
+
"twitch",
|
|
703
|
+
"keycloak",
|
|
704
|
+
"okta",
|
|
705
|
+
"ethereum",
|
|
706
|
+
"firebase",
|
|
707
|
+
"cognito",
|
|
708
|
+
"apikey",
|
|
709
|
+
"appleid",
|
|
710
|
+
"auth0",
|
|
711
|
+
"faceid",
|
|
712
|
+
"touchid",
|
|
713
|
+
"twofactor",
|
|
714
|
+
"backupcode",
|
|
715
|
+
"recoverycode",
|
|
716
|
+
"rememberme"
|
|
717
|
+
]);
|
|
718
|
+
var GRAPHQL_SIGN_IN_SUBJECT_WORDS = /* @__PURE__ */ new Set(["user", "customer", "admin", "member", "staff", "account"]);
|
|
719
|
+
var GRAPHQL_SIGN_IN_JOINERS = /* @__PURE__ */ new Set(["and", "or"]);
|
|
720
|
+
var GRAPHQL_SIGN_IN_JOINED_KINDS = /* @__PURE__ */ new Set([
|
|
721
|
+
"github",
|
|
722
|
+
"gitlab",
|
|
723
|
+
"linkedin",
|
|
724
|
+
"webauthn",
|
|
725
|
+
"passkey",
|
|
726
|
+
"apikey",
|
|
727
|
+
"idp",
|
|
728
|
+
"appleid",
|
|
729
|
+
"auth0",
|
|
730
|
+
"faceid",
|
|
731
|
+
"touchid",
|
|
732
|
+
"twofactor",
|
|
733
|
+
"backupcode",
|
|
734
|
+
"recoverycode",
|
|
735
|
+
"rememberme",
|
|
736
|
+
"username",
|
|
737
|
+
"userid"
|
|
738
|
+
]);
|
|
739
|
+
function joinSplitKinds(words) {
|
|
740
|
+
const out = [];
|
|
741
|
+
for (let i = 0; i < words.length; i++) {
|
|
742
|
+
let joined = 0;
|
|
743
|
+
for (let n = Math.min(3, words.length - i); n >= 2; n--) {
|
|
744
|
+
const part = words.slice(i, i + n);
|
|
745
|
+
if (!part.some((w) => GRAPHQL_SIGN_IN_JOINERS.has(w)) && GRAPHQL_SIGN_IN_JOINED_KINDS.has(part.join(""))) {
|
|
746
|
+
joined = n;
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
if (joined) {
|
|
751
|
+
out.push(words.slice(i, i + joined).join(""));
|
|
752
|
+
i += joined - 1;
|
|
753
|
+
} else
|
|
754
|
+
out.push(words[i]);
|
|
755
|
+
}
|
|
756
|
+
return out;
|
|
757
|
+
}
|
|
758
|
+
var GRAPHQL_SIGN_IN_LINK_KINDS = /* @__PURE__ */ new Set(["magic", "email"]);
|
|
759
|
+
function graphqlSignInTail(name) {
|
|
760
|
+
const words = wordsOf(name.replace(/(?<=[A-Z0-9][A-Z0-9])(?=[A-Z][a-z])/g, " "));
|
|
761
|
+
const head = words[0] === "log" || words[0] === "sign" ? words[1] === "in" ? [`${words[0]}in`, ...words.slice(2)] : null : words;
|
|
762
|
+
if (!head || head.length === 0)
|
|
763
|
+
return null;
|
|
764
|
+
const verb = GRAPHQL_SIGN_IN_VERBS.find((v) => head[0].startsWith(v));
|
|
765
|
+
if (!verb)
|
|
766
|
+
return null;
|
|
767
|
+
const glued = head[0].slice(verb.length);
|
|
768
|
+
return glued ? [glued, ...head.slice(1)] : head.slice(1);
|
|
769
|
+
}
|
|
770
|
+
function isGraphqlSignInTail(tail) {
|
|
771
|
+
const words = joinSplitKinds(tail);
|
|
772
|
+
let start = 0;
|
|
773
|
+
while (start < words.length && GRAPHQL_SIGN_IN_SUBJECT_WORDS.has(words[start]))
|
|
774
|
+
start++;
|
|
775
|
+
if (start === words.length)
|
|
776
|
+
return true;
|
|
777
|
+
let kinds = words.slice(start);
|
|
778
|
+
const phrase = kinds.findIndex((w) => GRAPHQL_SIGN_IN_PHRASE_WORDS.has(w));
|
|
779
|
+
if (phrase >= 0) {
|
|
780
|
+
if (phrase === kinds.length - 1 || phrase > 0 && GRAPHQL_SIGN_IN_JOINERS.has(kinds[phrase - 1]) || GRAPHQL_SIGN_IN_JOINERS.has(kinds[phrase + 1]))
|
|
781
|
+
return false;
|
|
782
|
+
kinds = [...kinds.slice(0, phrase), ...kinds.slice(phrase + 1)];
|
|
783
|
+
}
|
|
784
|
+
if (kinds.length === 0)
|
|
785
|
+
return false;
|
|
786
|
+
const isKind = (w, before) => GRAPHQL_SIGN_IN_TAIL_WORDS.has(w) || /^v\d{1,3}$/.test(w) || /^\d?fa$/.test(w) || w === "link" && before !== void 0 && GRAPHQL_SIGN_IN_LINK_KINDS.has(before);
|
|
787
|
+
return kinds.every((w, i) => GRAPHQL_SIGN_IN_JOINERS.has(w) ? i > 0 && i < kinds.length - 1 && !GRAPHQL_SIGN_IN_JOINERS.has(kinds[i - 1]) : isKind(w, i === phrase ? void 0 : kinds[i - 1]));
|
|
788
|
+
}
|
|
632
789
|
function wordsOf(segment) {
|
|
633
|
-
return segment.replace(/([a-z])(?=[A-Z0-9])/g, "$1 ").toLowerCase().split(/[\s
|
|
790
|
+
return segment.replace(/([a-z])(?=[A-Z0-9])/g, "$1 ").toLowerCase().split(/[\s.,_/-]+/).filter(Boolean);
|
|
634
791
|
}
|
|
635
792
|
function rawSegmentsOf(pathname) {
|
|
636
793
|
return pathname.split("/").map((seg) => {
|
|
@@ -651,7 +808,10 @@ var require_run_write_guard = __commonJS({
|
|
|
651
808
|
return words.some((w) => ACCOUNT_CHANGE_WORDS.has(w)) && words.some((w) => ACCOUNT_CHANGE_CONTEXT.has(w));
|
|
652
809
|
}
|
|
653
810
|
function isGraphqlSignInName(name) {
|
|
654
|
-
|
|
811
|
+
if (!GRAPHQL_SIGN_IN_NAME.test(name))
|
|
812
|
+
return false;
|
|
813
|
+
const tail = graphqlSignInTail(name);
|
|
814
|
+
return tail !== null && isGraphqlSignInTail(tail) && !changesSomething([name], { passwordIsCredential: true });
|
|
655
815
|
}
|
|
656
816
|
function graphqlSignIn(body) {
|
|
657
817
|
let parsed;
|
|
@@ -687,6 +847,173 @@ var require_run_write_guard = __commonJS({
|
|
|
687
847
|
}
|
|
688
848
|
return signIns > 0 ? "document" : null;
|
|
689
849
|
}
|
|
850
|
+
var NOT_A_BROKER_SIGN_IN_WORDS = /* @__PURE__ */ new Set([
|
|
851
|
+
"add",
|
|
852
|
+
"book",
|
|
853
|
+
"booking",
|
|
854
|
+
"bookings",
|
|
855
|
+
"order",
|
|
856
|
+
"orders",
|
|
857
|
+
"reserve",
|
|
858
|
+
"reservation",
|
|
859
|
+
"reservations",
|
|
860
|
+
"submit",
|
|
861
|
+
"send",
|
|
862
|
+
"invite",
|
|
863
|
+
"invites",
|
|
864
|
+
"invitation",
|
|
865
|
+
"invitations",
|
|
866
|
+
"checkout",
|
|
867
|
+
"purchase",
|
|
868
|
+
"purchases",
|
|
869
|
+
"buy",
|
|
870
|
+
"pay",
|
|
871
|
+
"payment",
|
|
872
|
+
"payments",
|
|
873
|
+
"cart",
|
|
874
|
+
"subscribe",
|
|
875
|
+
"subscription",
|
|
876
|
+
"subscriptions",
|
|
877
|
+
"publish",
|
|
878
|
+
"upload",
|
|
879
|
+
"charge",
|
|
880
|
+
"charges",
|
|
881
|
+
"refund",
|
|
882
|
+
"refunds"
|
|
883
|
+
]);
|
|
884
|
+
var BROKER_MAKE_VERBS = /* @__PURE__ */ new Set(["create", "generate"]);
|
|
885
|
+
var BROKER_CREDENTIAL_WORDS = /* @__PURE__ */ new Set(["session", "sessions", "login", "logins", "signin", "sign", "token", "tokens", "jwt", "jwts"]);
|
|
886
|
+
var BROKER_CREDENTIAL_QUALIFIERS = /* @__PURE__ */ new Set([
|
|
887
|
+
"access",
|
|
888
|
+
"refresh",
|
|
889
|
+
"id",
|
|
890
|
+
"auth",
|
|
891
|
+
"oauth",
|
|
892
|
+
"bearer",
|
|
893
|
+
"in",
|
|
894
|
+
"new",
|
|
895
|
+
"user",
|
|
896
|
+
"customer",
|
|
897
|
+
"with",
|
|
898
|
+
"from",
|
|
899
|
+
"via",
|
|
900
|
+
"for",
|
|
901
|
+
"by",
|
|
902
|
+
"and",
|
|
903
|
+
"mutation",
|
|
904
|
+
"google",
|
|
905
|
+
"apple",
|
|
906
|
+
"facebook",
|
|
907
|
+
"github",
|
|
908
|
+
"microsoft",
|
|
909
|
+
"sso",
|
|
910
|
+
"social",
|
|
911
|
+
"oidc",
|
|
912
|
+
"firebase"
|
|
913
|
+
]);
|
|
914
|
+
function namesOnlyACredential(words) {
|
|
915
|
+
const rest = words.filter((w) => !BROKER_MAKE_VERBS.has(w));
|
|
916
|
+
return rest.some((w) => BROKER_CREDENTIAL_WORDS.has(w)) && rest.every((w) => BROKER_CREDENTIAL_WORDS.has(w) || BROKER_CREDENTIAL_QUALIFIERS.has(w));
|
|
917
|
+
}
|
|
918
|
+
function actsBesidesBrokerSignIn(segments) {
|
|
919
|
+
return segments.some((seg, i) => {
|
|
920
|
+
const words = wordsOf(seg);
|
|
921
|
+
if (words.some((w) => NOT_A_BROKER_SIGN_IN_WORDS.has(w)) || NOT_A_BROKER_SIGN_IN_WORDS.has(words.join("")))
|
|
922
|
+
return true;
|
|
923
|
+
if (!words.some((w) => BROKER_MAKE_VERBS.has(w)))
|
|
924
|
+
return false;
|
|
925
|
+
if (namesOnlyACredential(words))
|
|
926
|
+
return false;
|
|
927
|
+
return !(words.length === 1 && i > 0 && i === segments.length - 1 && namesOnlyACredential(wordsOf(segments[i - 1])));
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
function brokerRefuses(segments) {
|
|
931
|
+
return changesSomething(segments) || actsBesidesBrokerSignIn(segments);
|
|
932
|
+
}
|
|
933
|
+
var BROKER_SIGN_IN_NAME_WORDS = /* @__PURE__ */ new Set(["login", "signin", "sign", "auth", "authenticate", "google", "oauth", "sso", "social", "session"]);
|
|
934
|
+
function isBrokerSignInName(name) {
|
|
935
|
+
return typeof name === "string" && wordsOf(name).some((w) => BROKER_SIGN_IN_NAME_WORDS.has(w)) && !brokerRefuses([name]);
|
|
936
|
+
}
|
|
937
|
+
function graphqlBrokerSignIn(body) {
|
|
938
|
+
let parsed;
|
|
939
|
+
try {
|
|
940
|
+
parsed = JSON.parse(body);
|
|
941
|
+
} catch {
|
|
942
|
+
parsed = { query: body };
|
|
943
|
+
}
|
|
944
|
+
const entries = Array.isArray(parsed) ? parsed : [parsed];
|
|
945
|
+
let mutationRoots = 0;
|
|
946
|
+
for (const entry of entries) {
|
|
947
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
948
|
+
return false;
|
|
949
|
+
const { query, operationName } = entry;
|
|
950
|
+
const record = entry;
|
|
951
|
+
const ids = PERSISTED_ID_KEYS.map((key) => record[key]).filter((n) => n !== void 0);
|
|
952
|
+
const names = operationName === void 0 ? ids : [operationName, ...ids];
|
|
953
|
+
if (ids.length > 0 && !names.every(isBrokerSignInName))
|
|
954
|
+
return false;
|
|
955
|
+
if (typeof query !== "string") {
|
|
956
|
+
return !Array.isArray(parsed) && query === void 0 && names.length > 0 && names.every(isBrokerSignInName);
|
|
957
|
+
}
|
|
958
|
+
const ops = (0, survey_network_guard_js_1.graphqlOperationRoots)(query);
|
|
959
|
+
if (!ops)
|
|
960
|
+
return false;
|
|
961
|
+
for (const op of ops) {
|
|
962
|
+
if (op.kind === "query")
|
|
963
|
+
continue;
|
|
964
|
+
if (typeof operationName === "string" && brokerRefuses([operationName]))
|
|
965
|
+
return false;
|
|
966
|
+
const roots = op.roots.filter((root) => root !== "__typename");
|
|
967
|
+
if (roots.some((root) => brokerRefuses([root])))
|
|
968
|
+
return false;
|
|
969
|
+
mutationRoots += roots.length;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return mutationRoots === 1;
|
|
973
|
+
}
|
|
974
|
+
var GRAPHQL_DOCUMENT_START = /^[\s,]*(?:[{#]|(?:query|mutation|subscription|fragment)\b)/;
|
|
975
|
+
function looksLikeGraphqlDocument(text) {
|
|
976
|
+
return GRAPHQL_DOCUMENT_START.test(text) || (0, survey_network_guard_js_1.graphqlOperationRoots)(text) !== null;
|
|
977
|
+
}
|
|
978
|
+
var PERSISTED_ID_KEYS = ["id", "documentId", "doc_id", "queryId"];
|
|
979
|
+
var GRAPHQL_ENVELOPE_FIELDS = ["variables", "operationName", "documentId", "doc_id", "queryId", "sha256Hash", "hash", "extensions", "operations"];
|
|
980
|
+
function hasGraphqlEnvelope(has, queryIsString) {
|
|
981
|
+
return queryIsString || GRAPHQL_ENVELOPE_FIELDS.some(has);
|
|
982
|
+
}
|
|
983
|
+
function carriesGraphql(search, body, contentType) {
|
|
984
|
+
if (search.getAll("query").some(looksLikeGraphqlDocument))
|
|
985
|
+
return true;
|
|
986
|
+
if (/^\s*application\/graphql\b/i.test(contentType ?? ""))
|
|
987
|
+
return true;
|
|
988
|
+
if (/multipart\//i.test(contentType ?? "") || /^\s*--/.test(body)) {
|
|
989
|
+
const parts = new Set([...body.matchAll(/\bname\s*=\s*"?([\w-]+)/gi)].map((m) => m[1]));
|
|
990
|
+
return hasGraphqlEnvelope((field) => parts.has(field), parts.has("query"));
|
|
991
|
+
}
|
|
992
|
+
let parsed;
|
|
993
|
+
try {
|
|
994
|
+
parsed = JSON.parse(body);
|
|
995
|
+
} catch {
|
|
996
|
+
if (looksLikeGraphqlDocument(body))
|
|
997
|
+
return true;
|
|
998
|
+
const form = new URLSearchParams(body);
|
|
999
|
+
return hasGraphqlEnvelope((field) => form.has(field), form.has("query"));
|
|
1000
|
+
}
|
|
1001
|
+
if (Array.isArray(parsed))
|
|
1002
|
+
return true;
|
|
1003
|
+
if (!parsed || typeof parsed !== "object")
|
|
1004
|
+
return false;
|
|
1005
|
+
const record = parsed;
|
|
1006
|
+
return hasGraphqlEnvelope((field) => record[field] !== void 0, typeof record.query === "string");
|
|
1007
|
+
}
|
|
1008
|
+
function isBrokerSignInShape(pathname, search, postData, contentType) {
|
|
1009
|
+
if (brokerRefuses(rawSegmentsOf(pathname)))
|
|
1010
|
+
return false;
|
|
1011
|
+
if (!isGraphqlPath(pathname) && !(typeof postData === "string" && carriesGraphql(search, postData, contentType)))
|
|
1012
|
+
return true;
|
|
1013
|
+
if ((0, survey_network_guard_js_1.graphqlUrlCarriesQuery)(search) || PERSISTED_ID_KEYS.some((key) => search.has(key)) || typeof postData !== "string" || postData.length > MAX_BODY_CHARS)
|
|
1014
|
+
return false;
|
|
1015
|
+
return graphqlBrokerSignIn(postData);
|
|
1016
|
+
}
|
|
690
1017
|
function isCredentialSignInShape(pathname, search, postData, contentType) {
|
|
691
1018
|
if (typeof postData === "string" && postData.length > MAX_BODY_CHARS)
|
|
692
1019
|
return false;
|
|
@@ -900,6 +1227,26 @@ var require_run_write_guard = __commonJS({
|
|
|
900
1227
|
}).join("");
|
|
901
1228
|
return { method, host, path: new RegExp(`^${body}/?$`, "i") };
|
|
902
1229
|
}
|
|
1230
|
+
function normaliseIdpHost(raw) {
|
|
1231
|
+
return raw.trim().toLowerCase().replace(/\.$/, "");
|
|
1232
|
+
}
|
|
1233
|
+
var WILDCARD_DNS_RE = /(?:^|\.)(?:nip\.io|sslip\.io|xip\.io|localtest\.me|lvh\.me|vcap\.me|traefik\.me|localho\.st|localhost\.direct|local\.gd|fuf\.me)$/;
|
|
1234
|
+
function idpHostProblem(host) {
|
|
1235
|
+
if (host.length > 253 || !/^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/.test(host))
|
|
1236
|
+
return "not a host name (no scheme, port, path or wildcard)";
|
|
1237
|
+
if (/^[0-9.]+$/.test(host))
|
|
1238
|
+
return "an IP address";
|
|
1239
|
+
if (/(?:^|\.)(?:localhost|local|internal)$/.test(host))
|
|
1240
|
+
return "a local name";
|
|
1241
|
+
if (WILDCARD_DNS_RE.test(host))
|
|
1242
|
+
return "a wildcard-DNS name (it can point at any address)";
|
|
1243
|
+
if ((0, shared_1.isSharedSuffix)(host))
|
|
1244
|
+
return exports.SHARED_SUFFIX_PATTERN_REASON;
|
|
1245
|
+
return null;
|
|
1246
|
+
}
|
|
1247
|
+
function isIdpHost(url, host, policy) {
|
|
1248
|
+
return url.protocol === "https:" && url.port === "" && (policy.idpHosts ?? []).includes(host);
|
|
1249
|
+
}
|
|
903
1250
|
function matchesAllowPatterns(method, host, pathname, policy, compiled) {
|
|
904
1251
|
const patterns = compiled ?? policy.allowPatterns.map(compileAllowPattern).filter((p) => p !== null);
|
|
905
1252
|
return patterns.some((p) => {
|
|
@@ -923,7 +1270,10 @@ var require_run_write_guard = __commonJS({
|
|
|
923
1270
|
return reason ? [{ pattern, reason }] : [];
|
|
924
1271
|
})
|
|
925
1272
|
];
|
|
926
|
-
return
|
|
1273
|
+
return [
|
|
1274
|
+
...ignored.map(({ pattern, reason }) => `[run-write-guard] SAFE_WRITE_PATTERNS entry ${JSON.stringify(pattern)} ignored: ${reason}`),
|
|
1275
|
+
...(policy.ignoredIdpHosts ?? []).map(({ pattern, reason }) => `[run-write-guard] AUTH_PROVIDER_HOSTS entry ${JSON.stringify(pattern)} ignored: ${reason}`)
|
|
1276
|
+
];
|
|
927
1277
|
}
|
|
928
1278
|
function describe(facts, url, rule) {
|
|
929
1279
|
return {
|
|
@@ -975,6 +1325,8 @@ var require_run_write_guard = __commonJS({
|
|
|
975
1325
|
return { action: "write", rule: "sign-up", record: describe(facts, url, "sign-up") };
|
|
976
1326
|
if (isAuthProviderWrite(method, host, pathname, facts.amzTarget, url.searchParams))
|
|
977
1327
|
return { action: "allow", rule: "auth-provider" };
|
|
1328
|
+
if (isIdpHost(url, host, policy) && isAuth0CustomDomainSignIn(method, pathname, url.searchParams))
|
|
1329
|
+
return { action: "allow", rule: "auth-provider" };
|
|
978
1330
|
const appSite = isAppSite(host, policy);
|
|
979
1331
|
if (facts.registeredAuthHandler && pathname === exports.FIREBASE_AUTH_HANDLER_PATH)
|
|
980
1332
|
return { action: "allow", rule: "auth-provider" };
|
|
@@ -982,7 +1334,7 @@ var require_run_write_guard = __commonJS({
|
|
|
982
1334
|
return { action: "allow", rule: "sign-in" };
|
|
983
1335
|
if (appSite && isAuth0CustomDomainSignIn(method, pathname, url.searchParams))
|
|
984
1336
|
return { action: "allow", rule: "sign-in" };
|
|
985
|
-
if (appSite && method === "POST" && facts.brokerTokenInBody &&
|
|
1337
|
+
if (appSite && method === "POST" && facts.brokerTokenInBody && isBrokerSignInShape(pathname, url.searchParams, facts.postData, facts.contentType)) {
|
|
986
1338
|
return { action: "allow", rule: "broker-sign-in" };
|
|
987
1339
|
}
|
|
988
1340
|
if (appSite && isEvidencedTokenRefresh(method, pathname, facts.postData))
|
|
@@ -990,7 +1342,7 @@ var require_run_write_guard = __commonJS({
|
|
|
990
1342
|
const signIn = appSite && method === "POST" && facts.signInSecretInBody ? isCredentialSignInShape(pathname, url.searchParams, facts.postData, facts.contentType) : false;
|
|
991
1343
|
if (signIn)
|
|
992
1344
|
return { action: "allow", rule: "credential-sign-in", ...signIn === "persisted" ? { persisted: true } : {} };
|
|
993
|
-
if (method === "POST" && isGraphqlPath(pathname) &&
|
|
1345
|
+
if (method === "POST" && isGraphqlPath(pathname) && (0, survey_network_guard_js_1.graphqlUrlQueriesAreReads)(url.searchParams) && isGraphqlQueryOnlyBody(facts.postData))
|
|
994
1346
|
return { action: "allow", rule: "graphql-query" };
|
|
995
1347
|
if (method === "POST" && hostIs(host, SEARCH_POST_HOSTS))
|
|
996
1348
|
return { action: "allow", rule: "search-post" };
|
|
@@ -1047,6 +1399,8 @@ var require_run_write_guard = __commonJS({
|
|
|
1047
1399
|
relayRejected = 0;
|
|
1048
1400
|
/** Credential sign-ins already said in the run log (observe mode; method, host and path shape). */
|
|
1049
1401
|
signInsLogged = /* @__PURE__ */ new Set();
|
|
1402
|
+
/** Hosts already named in the run log as a possible AUTH_PROVIDER_HOSTS entry (at most MAX_IDP_HINTS). */
|
|
1403
|
+
idpHintsLogged = /* @__PURE__ */ new Set();
|
|
1050
1404
|
constructor(policy, opts = {}) {
|
|
1051
1405
|
this.policy = policy;
|
|
1052
1406
|
this.compiled = policy.allowPatterns.map(compileAllowPattern).filter((p) => p !== null);
|
|
@@ -1144,6 +1498,22 @@ var require_run_write_guard = __commonJS({
|
|
|
1144
1498
|
const verb = held ? "held" : "saw";
|
|
1145
1499
|
const why = record.writeShaped ? `${record.rule}, ${record.writeShaped}` : record.rule;
|
|
1146
1500
|
this.log?.(`[run-write-guard] ${verb} ${record.method} ${record.host}${record.path}${record.keys.length ? ` (${record.keys.join(", ")})` : ""} [${why}]`);
|
|
1501
|
+
this.hintIdpHost(record);
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* An Auth0 sign-in step (by path) posted to another site was a third-party write: the tenant may
|
|
1505
|
+
* answer on a custom domain of its own. Said once per host, for at most MAX_IDP_HINTS hosts, so a
|
|
1506
|
+
* person knows AUTH_PROVIDER_HOSTS exists; the guard never names the host itself.
|
|
1507
|
+
*/
|
|
1508
|
+
hintIdpHost(record) {
|
|
1509
|
+
if (record.rule !== "third-party-write" || record.method !== "POST")
|
|
1510
|
+
return;
|
|
1511
|
+
if (this.idpHintsLogged.has(record.host) || this.idpHintsLogged.size >= MAX_IDP_HINTS)
|
|
1512
|
+
return;
|
|
1513
|
+
if ((this.policy.idpHosts ?? []).includes(record.host) || !AUTH0_AUTH_RE.test(record.path.toLowerCase()))
|
|
1514
|
+
return;
|
|
1515
|
+
this.idpHintsLogged.add(record.host);
|
|
1516
|
+
this.log?.(`[run-write-guard] note: ${record.host} looks like an Auth0 sign-in on another site; if it is the app's own sign-in, a project admin can name it in AUTH_PROVIDER_HOSTS in Test Data`);
|
|
1147
1517
|
}
|
|
1148
1518
|
/**
|
|
1149
1519
|
* A page sent a message over a WebSocket to the app's site. The guard cannot tell a read from a
|
|
@@ -1951,8 +2321,9 @@ var require_run_write_guard = __commonJS({
|
|
|
1951
2321
|
};
|
|
1952
2322
|
Object.assign(context, {
|
|
1953
2323
|
route: async (url, handler, options) => {
|
|
1954
|
-
await route(url, handler, options);
|
|
2324
|
+
const disposable = await route(url, handler, options);
|
|
1955
2325
|
others.push({ url, handler });
|
|
2326
|
+
return disposable;
|
|
1956
2327
|
},
|
|
1957
2328
|
unroute: async (url, handler) => {
|
|
1958
2329
|
if (handler === void 0 && sameRouteUrl(url, GUARD_ROUTE)) {
|
|
@@ -2285,6 +2656,7 @@ var require_run_write_guard = __commonJS({
|
|
|
2285
2656
|
}
|
|
2286
2657
|
function buildRunWriteGuardHook(policy, modulePath, signInSecretNames = [], opts = {}) {
|
|
2287
2658
|
const withContext = opts.browserContext !== false;
|
|
2659
|
+
const hookTest = opts.hookTest ?? "test";
|
|
2288
2660
|
const names = signInSecretNames.filter((n) => /^[A-Za-z_][A-Za-z0-9_]{0,63}$/.test(n)).slice(0, 16);
|
|
2289
2661
|
return `
|
|
2290
2662
|
import { createRequire as __qaWriteGuardRequire } from 'node:module';
|
|
@@ -2307,7 +2679,7 @@ try {
|
|
|
2307
2679
|
} catch (err) {
|
|
2308
2680
|
__qaWriteGuardError = err;
|
|
2309
2681
|
}
|
|
2310
|
-
|
|
2682
|
+
${hookTest}.beforeEach(async (${withContext ? "{ context }" : ""}) => {
|
|
2311
2683
|
try {
|
|
2312
2684
|
if (__qaWriteGuardError) throw __qaWriteGuardError;
|
|
2313
2685
|
${withContext ? "await __qaWriteGuard.installRunWriteGuard(context, __qaWriteGuardPolicy, { onLog: (line) => console.log(line), signInSecrets: __qaWriteGuardSecrets });" : "// an @api test: no browser context, only Node's fetch is guarded"}
|
|
@@ -2316,7 +2688,7 @@ test.beforeEach(async (${withContext ? "{ context }" : ""}) => {
|
|
|
2316
2688
|
${policy.mode === "block" ? "throw err;" : "// observe: never fail the test"}
|
|
2317
2689
|
}
|
|
2318
2690
|
});${withContext && policy.mode === "block" ? `
|
|
2319
|
-
|
|
2691
|
+
${hookTest}.afterEach(async ({ context }) => {
|
|
2320
2692
|
try { if (__qaWriteGuard) await __qaWriteGuard.clearRunWriteRelay(context); } catch { /* the context closes next */ }
|
|
2321
2693
|
});` : ""}
|
|
2322
2694
|
`;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
require_route_manifest
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PWFWMVZU.mjs";
|
|
4
4
|
import {
|
|
5
5
|
require_survey_network_guard
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-E2RRAE27.mjs";
|
|
7
7
|
import {
|
|
8
8
|
dist_exports,
|
|
9
9
|
init_dist
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-3JM6XTSZ.mjs";
|
|
11
11
|
import {
|
|
12
12
|
__commonJS,
|
|
13
13
|
__toCommonJS
|
|
@@ -20,6 +20,7 @@ var require_exploration_parser = __commonJS({
|
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.extractCodeLines = extractCodeLines;
|
|
22
22
|
exports.extractProvenLocator = extractProvenLocator;
|
|
23
|
+
exports.maskTypedValues = maskTypedValues;
|
|
23
24
|
exports.sanitizePromptCodeLine = sanitizePromptCodeLine;
|
|
24
25
|
exports.extractPageUrl = extractPageUrl;
|
|
25
26
|
exports.extractPageTitle = extractPageTitle;
|
|
@@ -34,7 +35,7 @@ var require_exploration_parser = __commonJS({
|
|
|
34
35
|
var shared_1 = (init_dist(), __toCommonJS(dist_exports));
|
|
35
36
|
function extractCodeLines(toolResultContent) {
|
|
36
37
|
const isPlaywrightLine = (l) => l.startsWith("await page.") || l.startsWith("await expect(");
|
|
37
|
-
const codeSection = toolResultContent.match(/###\s*Ran Playwright code\s*\n([\s\S]*?)(?=\n###|\n$|$)/);
|
|
38
|
+
const codeSection = toolResultContent.match(/###\s*Ran Playwright code\s*\n(?![^\S\n]*\n)([\s\S]*?)(?=\n###|\n$|$)/);
|
|
38
39
|
if (codeSection) {
|
|
39
40
|
return codeSection[1].split("\n").map((l) => l.trim()).filter(isPlaywrightLine).map((l) => l.replace(/;$/, ""));
|
|
40
41
|
}
|
|
@@ -78,7 +79,7 @@ var require_exploration_parser = __commonJS({
|
|
|
78
79
|
cut = idx;
|
|
79
80
|
}
|
|
80
81
|
let locator = cut > start ? codeLine.slice(start, cut) : codeLine.slice(start);
|
|
81
|
-
locator = locator.trim().replace(
|
|
82
|
+
locator = locator.trim().replace(/(?<!;);+\s*$/, "").trim();
|
|
82
83
|
if (!locator.includes("("))
|
|
83
84
|
return null;
|
|
84
85
|
if (!/^(getBy\w+|locator)\(/.test(locator))
|
|
@@ -158,13 +159,16 @@ var require_exploration_parser = __commonJS({
|
|
|
158
159
|
return false;
|
|
159
160
|
return RECORD_TITLE_WITH_COMPANY.test(name);
|
|
160
161
|
}
|
|
162
|
+
var TYPED_VALUE_CALL_RE = /\.(fill|type|pressSequentially|insertText)\(\s*(['"])(?:\\.|(?!\2)[^\\])*\2(\s*,\s*\{[^{}]*\})?\s*\)/g;
|
|
163
|
+
function maskTypedValues(codeLine, mask) {
|
|
164
|
+
return codeLine.replace(TYPED_VALUE_CALL_RE, (_all, method, _quote, options) => `.${method}(${mask}${options ?? ""})`);
|
|
165
|
+
}
|
|
161
166
|
function sanitizePromptCodeLine(codeLine) {
|
|
162
167
|
let sanitized = codeLine;
|
|
163
168
|
sanitized = sanitized.replace(/getByRole\('(textbox|searchbox|combobox|spinbutton)',\s*\{\s*name:\s*['"]([^'"]*)['"]\s*\}\)/g, (match, role, name, _offset, line) => looksDynamicFieldLabel(name, line) ? `getByRole('${role}')` : match);
|
|
164
169
|
sanitized = sanitized.replace(/getByLabel\(\s*(['"])([^'"]*)\1\s*\)(?=\)?\.(?:fill|type|toHaveValue|inputValue|press)\()/g, (match, _quote, label, _offset, line) => looksDynamicFieldLabel(label, line) ? "getByRole('textbox')" : match);
|
|
165
170
|
sanitized = sanitized.replace(/getByLabel\(\s*(['"])([^'"]*)\1\s*\)(?=\)?\.selectOption\()/g, (match, _quote, label, _offset, line) => looksDynamicFieldLabel(label, line) ? "getByRole('combobox')" : match);
|
|
166
|
-
sanitized = sanitized
|
|
167
|
-
sanitized = sanitized.replace(/\.type\(\s*(['"]).*?\1\s*\)/g, ".type('<test-value>')");
|
|
171
|
+
sanitized = maskTypedValues(sanitized, "'<test-value>'");
|
|
168
172
|
sanitized = sanitized.replace(/\.toHaveValue\(\s*(['"]).*?\1\s*\)/g, ".toHaveValue(/./)");
|
|
169
173
|
sanitized = sanitized.replace(/getByRole\('heading',\s*\{\s*name:\s*'([^']*)'\s*\}\)/g, (match, name) => looksDynamicRecordTitle(name) ? "getByRole('heading')" : match);
|
|
170
174
|
sanitized = sanitized.replace(/getByRole\('heading',\s*\{\s*name:\s*"([^"]*)"\s*\}\)/g, (match, name) => looksDynamicRecordTitle(name) ? "getByRole('heading')" : match);
|
|
@@ -188,7 +192,7 @@ var require_exploration_parser = __commonJS({
|
|
|
188
192
|
return match ? match[1].trim() : null;
|
|
189
193
|
}
|
|
190
194
|
function extractSnapshotSection(content) {
|
|
191
|
-
const match = content.match(/###\s*Snapshot\s*\n([\s\S]*?)(?=\n###|$)/);
|
|
195
|
+
const match = content.match(/###\s*Snapshot\s*\n(?![^\S\n]*\n)([\s\S]*?)(?=\n###|$)/);
|
|
192
196
|
if (!match)
|
|
193
197
|
return null;
|
|
194
198
|
if (/^\s*- \[Snapshot\]\([^)\n]*\)\s*$/.test(match[1]))
|
|
@@ -256,7 +260,7 @@ var require_exploration_parser = __commonJS({
|
|
|
256
260
|
return hints;
|
|
257
261
|
}
|
|
258
262
|
function extractFirstAlert(snapshotYaml) {
|
|
259
|
-
const match = snapshotYaml.match(
|
|
263
|
+
const match = snapshotYaml.match(/^[^\S\n\r\u2028\u2029]*-\s+(?:alert|status)\s+"([^"]+)"/m);
|
|
260
264
|
return match ? match[1] : null;
|
|
261
265
|
}
|
|
262
266
|
function buildInteractionLogEntry(toolName, toolArgs, codeLines, pageUrl, pageTitle, snapshotSection) {
|
|
@@ -297,6 +301,12 @@ var require_exploration_parser = __commonJS({
|
|
|
297
301
|
return /^\d{10,}$/.test(value);
|
|
298
302
|
return false;
|
|
299
303
|
}
|
|
304
|
+
function hashRouterPathWithoutTrailingSlash(hash) {
|
|
305
|
+
const q = hash.indexOf("?");
|
|
306
|
+
const path = q < 0 ? hash : hash.slice(0, q);
|
|
307
|
+
const stripped = (0, shared_1.stripTrailingSlashes)(path);
|
|
308
|
+
return stripped === "#" || stripped === "" ? hash : stripped + (q < 0 ? "" : hash.slice(q));
|
|
309
|
+
}
|
|
300
310
|
function normalizeRoute(url) {
|
|
301
311
|
let route = url.pathname;
|
|
302
312
|
const encodeQueryPart = (part) => part.replace(/[%&=#+?]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`);
|
|
@@ -315,7 +325,7 @@ var require_exploration_parser = __commonJS({
|
|
|
315
325
|
}
|
|
316
326
|
}
|
|
317
327
|
if (url.hash && url.hash !== "#" && url.hash !== "#/") {
|
|
318
|
-
route += url.hash;
|
|
328
|
+
route += url.hash.startsWith("#/") ? hashRouterPathWithoutTrailingSlash(url.hash) : url.hash;
|
|
319
329
|
}
|
|
320
330
|
return route;
|
|
321
331
|
}
|