@wenathlan/extension 1.1.42 → 1.1.44
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 +6 -4
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1451 -14
- package/dist/index.js.map +4 -4
- package/dist/memory.d.ts +76 -5
- package/dist/memory.d.ts.map +1 -1
- package/dist/netauth.d.ts +46 -0
- package/dist/netauth.d.ts.map +1 -0
- package/dist/netcontrol.d.ts +96 -0
- package/dist/netcontrol.d.ts.map +1 -0
- package/dist/netwatch.d.ts +92 -0
- package/dist/netwatch.d.ts.map +1 -0
- package/dist/policy.d.ts +31 -0
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +59 -3
- package/dist/protocol.d.ts.map +1 -1
- package/dist/socketbus.d.ts +134 -0
- package/dist/socketbus.d.ts.map +1 -0
- package/dist/types.d.ts +338 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +2890 -630
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +41 -4
- package/extension/dist/pagebridge.js.map +2 -2
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +20 -3
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +212 -3
- package/extension/dist/sidepanel.js.map +2 -2
- package/extension/dist/style.css +1 -0
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1648,15 +1648,22 @@ var sessionmemory = class {
|
|
|
1648
1648
|
async getfetchconsents() {
|
|
1649
1649
|
return await this.adapter.get("fetchconsents") ?? [];
|
|
1650
1650
|
}
|
|
1651
|
-
/** Stores one api key
|
|
1652
|
-
async setapikey(
|
|
1653
|
-
const records = (await this.adapter.get("apikeys") ?? []).filter((item) => item.name !==
|
|
1654
|
-
await this.adapter.set("apikeys", [
|
|
1651
|
+
/** Stores one api key entry with its origin scope and created time, replacing the previous entry of that name; the key material stays behind its storage id. */
|
|
1652
|
+
async setapikey(entry) {
|
|
1653
|
+
const records = (await this.adapter.get("apikeys") ?? []).filter((item) => item.name !== entry.name);
|
|
1654
|
+
await this.adapter.set("apikeys", [entry, ...records]);
|
|
1655
1655
|
}
|
|
1656
|
-
/** Returns every stored api key
|
|
1656
|
+
/** Returns every stored api key entry with its origin scope, header name, storage id and last use timestamp; key material never loads here. */
|
|
1657
1657
|
async getapikeys() {
|
|
1658
1658
|
return await this.adapter.get("apikeys") ?? [];
|
|
1659
1659
|
}
|
|
1660
|
+
/** Stamps the last use timestamp of one stored api key entry without ever loading the key material. */
|
|
1661
|
+
async touchapikey(name, at) {
|
|
1662
|
+
const records = await this.getapikeys();
|
|
1663
|
+
const entry = records.find((item) => item.name === name);
|
|
1664
|
+
if (!entry) return;
|
|
1665
|
+
await this.adapter.set("apikeys", [{ ...entry, lastuse: at }, ...records.filter((item) => item.name !== name)]);
|
|
1666
|
+
}
|
|
1660
1667
|
/** Removes one api key reference and its stored secret together. */
|
|
1661
1668
|
async removeapikey(name) {
|
|
1662
1669
|
const records = await this.getapikeys();
|
|
@@ -1672,6 +1679,155 @@ var sessionmemory = class {
|
|
|
1672
1679
|
async getsecret(storageid) {
|
|
1673
1680
|
return this.adapter.get(storageid);
|
|
1674
1681
|
}
|
|
1682
|
+
/** Stores one channel record of a socket or event stream, replacing the previous record of that id. */
|
|
1683
|
+
async addchannel(record2) {
|
|
1684
|
+
const records = (await this.adapter.get("channels") ?? []).filter((item) => item.id !== record2.id);
|
|
1685
|
+
await this.adapter.set("channels", [record2, ...records]);
|
|
1686
|
+
}
|
|
1687
|
+
/** Returns every stored channel record, newest first. */
|
|
1688
|
+
async getchannels() {
|
|
1689
|
+
return await this.adapter.get("channels") ?? [];
|
|
1690
|
+
}
|
|
1691
|
+
/** Returns one channel record by its id. */
|
|
1692
|
+
async getchannel(id) {
|
|
1693
|
+
return (await this.getchannels()).find((item) => item.id === id);
|
|
1694
|
+
}
|
|
1695
|
+
/** Queues one message envelope of a channel stream, keeping the arrival order for the waitmessage matchers. */
|
|
1696
|
+
async addmessage(envelope) {
|
|
1697
|
+
const records = (await this.adapter.get("messages") ?? []).filter((item) => !(item.channelid === envelope.channelid && item.sequence === envelope.sequence));
|
|
1698
|
+
await this.adapter.set("messages", [...records, envelope]);
|
|
1699
|
+
}
|
|
1700
|
+
/** Returns every queued message envelope, oldest first, optionally filtered by channel and stream. */
|
|
1701
|
+
async getmessages(channelid, stream) {
|
|
1702
|
+
const records = await this.adapter.get("messages") ?? [];
|
|
1703
|
+
return records.filter((item) => (channelid === void 0 || item.channelid === channelid) && (stream === void 0 || item.stream === stream));
|
|
1704
|
+
}
|
|
1705
|
+
/** Drops the matched message envelopes of one channel from the queue once a waitmessage step consumed them. */
|
|
1706
|
+
async drainmessages(sequences) {
|
|
1707
|
+
const records = await this.adapter.get("messages") ?? [];
|
|
1708
|
+
const kept = records.filter((item) => !sequences.some((match) => match.channelid === item.channelid && match.sequence === item.sequence));
|
|
1709
|
+
await this.adapter.set("messages", kept);
|
|
1710
|
+
}
|
|
1711
|
+
/** Stores one observed exchange record, replacing the previous record of that id. */
|
|
1712
|
+
async addexchange(record2) {
|
|
1713
|
+
const records = (await this.adapter.get("exchanges") ?? []).filter((item) => item.id !== record2.id);
|
|
1714
|
+
await this.adapter.set("exchanges", [record2, ...records]);
|
|
1715
|
+
}
|
|
1716
|
+
/** Returns every stored exchange record, newest first. */
|
|
1717
|
+
async getexchanges() {
|
|
1718
|
+
return await this.adapter.get("exchanges") ?? [];
|
|
1719
|
+
}
|
|
1720
|
+
/** Returns one exchange record by its id. */
|
|
1721
|
+
async getexchange(id) {
|
|
1722
|
+
return (await this.getexchanges()).find((item) => item.id === id);
|
|
1723
|
+
}
|
|
1724
|
+
/** Returns the exchange records filtered by run, origin and status; the status filter accepts one code or the failed class of every exchange with an error class. */
|
|
1725
|
+
async listexchanges(filter) {
|
|
1726
|
+
const records = await this.getexchanges();
|
|
1727
|
+
return records.filter((item) => (filter.runid === void 0 || item.runid === filter.runid) && (filter.origin === void 0 || item.origin === filter.origin) && (filter.status === void 0 || (filter.status === "failed" ? item.errorclass !== void 0 : item.status === filter.status)));
|
|
1728
|
+
}
|
|
1729
|
+
/** Stores one captured response body with its mime type and byte size; the user configured body retention window expires the oldest bodies while the exchange metadata always survives. */
|
|
1730
|
+
async addbody(record2) {
|
|
1731
|
+
const records = await this.getbodies();
|
|
1732
|
+
const retention = (await this.getsettings())?.bodyretention;
|
|
1733
|
+
const combined = [record2, ...records.filter((item) => item.ref !== record2.ref)];
|
|
1734
|
+
const stored = retention === void 0 ? combined : combined.map((item, index) => index < retention ? item : expirebodybytes(item));
|
|
1735
|
+
await this.adapter.set("bodies", stored);
|
|
1736
|
+
}
|
|
1737
|
+
/** Returns every captured body record, newest first. */
|
|
1738
|
+
async getbodies() {
|
|
1739
|
+
return await this.adapter.get("bodies") ?? [];
|
|
1740
|
+
}
|
|
1741
|
+
/** Returns one captured body record with its stored text by its reference. */
|
|
1742
|
+
async getbody(ref) {
|
|
1743
|
+
return (await this.getbodies()).find((item) => item.ref === ref);
|
|
1744
|
+
}
|
|
1745
|
+
/** Stores the page api map of one origin, replacing the previous map of that origin. */
|
|
1746
|
+
async setapimap(origin, entries) {
|
|
1747
|
+
const records = (await this.adapter.get("apimap") ?? []).filter((item) => item.origin !== origin);
|
|
1748
|
+
await this.adapter.set("apimap", [...entries, ...records]);
|
|
1749
|
+
}
|
|
1750
|
+
/** Returns every stored page api map entry, newest first. */
|
|
1751
|
+
async getapimap() {
|
|
1752
|
+
return await this.adapter.get("apimap") ?? [];
|
|
1753
|
+
}
|
|
1754
|
+
/** Stores one event stream subscription record, replacing the previous record of that id. */
|
|
1755
|
+
async setsubscription(record2) {
|
|
1756
|
+
const records = (await this.adapter.get("subscriptions") ?? []).filter((item) => item.id !== record2.id);
|
|
1757
|
+
await this.adapter.set("subscriptions", [record2, ...records]);
|
|
1758
|
+
}
|
|
1759
|
+
/** Returns every stored event stream subscription, newest first. */
|
|
1760
|
+
async getsubscriptions() {
|
|
1761
|
+
return await this.adapter.get("subscriptions") ?? [];
|
|
1762
|
+
}
|
|
1763
|
+
/** Stores one registered request block rule of a run, replacing the previous rule of that id; every rule reverts and stays auditable after the run ends. */
|
|
1764
|
+
async addblockrule(rule) {
|
|
1765
|
+
const records = (await this.adapter.get("blockrules") ?? []).filter((item) => item.id !== rule.id);
|
|
1766
|
+
await this.adapter.set("blockrules", [rule, ...records]);
|
|
1767
|
+
}
|
|
1768
|
+
/** Returns every stored block rule, newest first. */
|
|
1769
|
+
async getblockrules() {
|
|
1770
|
+
return await this.adapter.get("blockrules") ?? [];
|
|
1771
|
+
}
|
|
1772
|
+
/** Stores one registered response mock fixture of a run, replacing the previous fixture of that id; the fixture body stays out of every audit trail. */
|
|
1773
|
+
async addmockspec(spec) {
|
|
1774
|
+
const records = (await this.adapter.get("mockspecs") ?? []).filter((item) => item.id !== spec.id);
|
|
1775
|
+
await this.adapter.set("mockspecs", [spec, ...records]);
|
|
1776
|
+
}
|
|
1777
|
+
/** Returns every stored mock fixture, newest first. */
|
|
1778
|
+
async getmockspecs() {
|
|
1779
|
+
return await this.adapter.get("mockspecs") ?? [];
|
|
1780
|
+
}
|
|
1781
|
+
/** Stores one registered header rewrite rule of a run, replacing the previous rule of that id; the provenance of every applied rule stays auditable. */
|
|
1782
|
+
async addheaderule(rule) {
|
|
1783
|
+
const records = (await this.adapter.get("headerules") ?? []).filter((item) => item.id !== rule.id);
|
|
1784
|
+
await this.adapter.set("headerules", [rule, ...records]);
|
|
1785
|
+
}
|
|
1786
|
+
/** Returns every stored header rewrite rule, newest first. */
|
|
1787
|
+
async getheaderules() {
|
|
1788
|
+
return await this.adapter.get("headerules") ?? [];
|
|
1789
|
+
}
|
|
1790
|
+
/** Stores one cookie operation of a run per domain with its timestamp; cookie values never enter the operation record. */
|
|
1791
|
+
async addcookieop(operation) {
|
|
1792
|
+
const records = await this.adapter.get("cookieops") ?? [];
|
|
1793
|
+
await this.adapter.set("cookieops", [operation, ...records]);
|
|
1794
|
+
}
|
|
1795
|
+
/** Returns every stored cookie operation, newest first, optionally filtered by domain. */
|
|
1796
|
+
async getcookieops(domain) {
|
|
1797
|
+
const records = await this.adapter.get("cookieops") ?? [];
|
|
1798
|
+
return records.filter((item) => domain === void 0 || item.domain === domain);
|
|
1799
|
+
}
|
|
1800
|
+
/** Stores one token record of a provider with scopes, origin scope and expiry; the token values stay behind their storage ids. */
|
|
1801
|
+
async addtoken(record2) {
|
|
1802
|
+
const records = (await this.adapter.get("tokens") ?? []).filter((item) => item.id !== record2.id);
|
|
1803
|
+
await this.adapter.set("tokens", [record2, ...records]);
|
|
1804
|
+
}
|
|
1805
|
+
/** Returns every stored token record, newest first, optionally filtered by provider; token values never load here. */
|
|
1806
|
+
async listtokens(provider) {
|
|
1807
|
+
const records = await this.adapter.get("tokens") ?? [];
|
|
1808
|
+
return records.filter((item) => provider === void 0 || item.provider === provider);
|
|
1809
|
+
}
|
|
1810
|
+
/** Stores one applied proxy route of a run with its apply time, replacing the previous route of that id; the history keeps the revert times. */
|
|
1811
|
+
async addproxyroute(route) {
|
|
1812
|
+
const records = (await this.adapter.get("proxyroutes") ?? []).filter((item) => item.id !== route.id);
|
|
1813
|
+
await this.adapter.set("proxyroutes", [route, ...records]);
|
|
1814
|
+
}
|
|
1815
|
+
/** Returns every stored proxy route with apply and revert times, newest first. */
|
|
1816
|
+
async getproxyroutes() {
|
|
1817
|
+
return await this.adapter.get("proxyroutes") ?? [];
|
|
1818
|
+
}
|
|
1819
|
+
/** Stores one parsed rate limit read per origin, replacing the previous read of that origin. */
|
|
1820
|
+
async setratelimit(read) {
|
|
1821
|
+
const records = (await this.adapter.get("ratelimits") ?? []).filter((item) => item.origin !== read.origin);
|
|
1822
|
+
await this.adapter.set("ratelimits", [read, ...records]);
|
|
1823
|
+
}
|
|
1824
|
+
/** Returns every stored rate limit read whose reset window has not passed yet; expired states drop out at their reset windows. */
|
|
1825
|
+
async getratelimits(now) {
|
|
1826
|
+
const records = await this.adapter.get("ratelimits") ?? [];
|
|
1827
|
+
const live = records.filter((item) => item.resetat > now);
|
|
1828
|
+
if (live.length !== records.length) await this.adapter.set("ratelimits", live);
|
|
1829
|
+
return live;
|
|
1830
|
+
}
|
|
1675
1831
|
};
|
|
1676
1832
|
function mediakindof(record2) {
|
|
1677
1833
|
if ("pages" in record2) return "pdf";
|
|
@@ -1706,14 +1862,732 @@ function expirecallbody(record2) {
|
|
|
1706
1862
|
void body;
|
|
1707
1863
|
return { ...metadata, bodyexpired: true };
|
|
1708
1864
|
}
|
|
1865
|
+
function expirebodybytes(record2) {
|
|
1866
|
+
const { body, ...metadata } = record2;
|
|
1867
|
+
void body;
|
|
1868
|
+
return { ...metadata, bodyexpired: true };
|
|
1869
|
+
}
|
|
1709
1870
|
function randomid() {
|
|
1710
1871
|
return crypto.randomUUID();
|
|
1711
1872
|
}
|
|
1712
1873
|
|
|
1874
|
+
// netauth.ts
|
|
1875
|
+
function oauthflowof(value) {
|
|
1876
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1877
|
+
const options = value;
|
|
1878
|
+
if (typeof options.provider !== "string" || !options.provider.trim()) return void 0;
|
|
1879
|
+
if (typeof options.authorizeurl !== "string" || !options.authorizeurl.trim()) return void 0;
|
|
1880
|
+
if (typeof options.tokenurl !== "string" || !options.tokenurl.trim()) return void 0;
|
|
1881
|
+
if (!Array.isArray(options.scopes) || options.scopes.length === 0 || !options.scopes.every((item) => typeof item === "string" && item.trim().length > 0)) return void 0;
|
|
1882
|
+
if (typeof options.redirectorigin !== "string" || !options.redirectorigin.trim()) return void 0;
|
|
1883
|
+
return { provider: options.provider.trim(), authorizeurl: options.authorizeurl.trim(), tokenurl: options.tokenurl.trim(), scopes: options.scopes.map((item) => item.trim()), redirectorigin: options.redirectorigin.trim() };
|
|
1884
|
+
}
|
|
1885
|
+
function authorizeurl(flow, state) {
|
|
1886
|
+
const url = new URL(flow.authorizeurl);
|
|
1887
|
+
url.searchParams.set("response_type", "code");
|
|
1888
|
+
url.searchParams.set("redirect_uri", flow.redirectorigin);
|
|
1889
|
+
url.searchParams.set("scope", flow.scopes.join(" "));
|
|
1890
|
+
url.searchParams.set("state", state);
|
|
1891
|
+
return url.toString();
|
|
1892
|
+
}
|
|
1893
|
+
function capturecode(url, redirectorigin, state) {
|
|
1894
|
+
let parsed;
|
|
1895
|
+
try {
|
|
1896
|
+
parsed = new URL(url);
|
|
1897
|
+
} catch {
|
|
1898
|
+
return { error: "The redirect url does not parse for the code capture." };
|
|
1899
|
+
}
|
|
1900
|
+
const granted = redirectorigin.includes("/", redirectorigin.indexOf("://") + 3) ? `${parsed.origin}${parsed.pathname}`.startsWith(redirectorigin) : parsed.origin === redirectorigin;
|
|
1901
|
+
if (!granted) return { error: `The redirect landed on ${parsed.origin} outside the granted redirect origin ${redirectorigin}.` };
|
|
1902
|
+
const returned = parsed.searchParams.get("state");
|
|
1903
|
+
if (returned !== state) return { error: "The redirect state token does not match the reviewed flow." };
|
|
1904
|
+
const error = parsed.searchParams.get("error");
|
|
1905
|
+
if (error) return { error: `The provider refused the flow: ${error}.` };
|
|
1906
|
+
const code = parsed.searchParams.get("code");
|
|
1907
|
+
if (!code) return { error: "The redirect carries no authorization code." };
|
|
1908
|
+
return { code };
|
|
1909
|
+
}
|
|
1910
|
+
function parsetokens(body) {
|
|
1911
|
+
let parsed;
|
|
1912
|
+
try {
|
|
1913
|
+
parsed = JSON.parse(body);
|
|
1914
|
+
} catch {
|
|
1915
|
+
return void 0;
|
|
1916
|
+
}
|
|
1917
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
1918
|
+
const record2 = parsed;
|
|
1919
|
+
const tokens = {};
|
|
1920
|
+
if (typeof record2.access_token === "string" && record2.access_token) tokens.accesstoken = record2.access_token;
|
|
1921
|
+
if (typeof record2.refresh_token === "string" && record2.refresh_token) tokens.refreshtoken = record2.refresh_token;
|
|
1922
|
+
if (typeof record2.expires_in === "number" && Number.isFinite(record2.expires_in) && record2.expires_in >= 0) tokens.expiresin = record2.expires_in;
|
|
1923
|
+
if (typeof record2.scope === "string" && record2.scope.trim()) tokens.scopes = record2.scope.trim().split(/\s+/);
|
|
1924
|
+
if (tokens.accesstoken === void 0 && tokens.refreshtoken === void 0) return void 0;
|
|
1925
|
+
return tokens;
|
|
1926
|
+
}
|
|
1927
|
+
function tokenrequest(flow, input) {
|
|
1928
|
+
if (input.refreshtoken !== void 0) return { url: flow.tokenurl, body: urlencodeform([{ name: "grant_type", value: "refresh_token" }, { name: "refresh_token", value: input.refreshtoken }]) };
|
|
1929
|
+
return { url: flow.tokenurl, body: urlencodeform([{ name: "grant_type", value: "authorization_code" }, { name: "code", value: input.code ?? "" }, { name: "redirect_uri", value: flow.redirectorigin }]) };
|
|
1930
|
+
}
|
|
1931
|
+
function revocationruleof(value) {
|
|
1932
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1933
|
+
const options = value;
|
|
1934
|
+
if (!Array.isArray(options.tokenids) || options.tokenids.length === 0 || !options.tokenids.every((item) => typeof item === "string" && item.trim().length > 0)) return void 0;
|
|
1935
|
+
if (typeof options.reason !== "string" || !options.reason.trim()) return void 0;
|
|
1936
|
+
return { tokenids: options.tokenids.map((item) => item.trim()), reason: options.reason.trim(), revokedat: Date.now() };
|
|
1937
|
+
}
|
|
1938
|
+
function formpayloadof(value) {
|
|
1939
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1940
|
+
const options = value;
|
|
1941
|
+
if (typeof options.url !== "string" || !options.url.trim()) return void 0;
|
|
1942
|
+
if (!Array.isArray(options.fields) || options.fields.length === 0) return void 0;
|
|
1943
|
+
const fields = [];
|
|
1944
|
+
for (const item of options.fields) {
|
|
1945
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return void 0;
|
|
1946
|
+
const field = item;
|
|
1947
|
+
if (typeof field.name !== "string" || !field.name.trim()) return void 0;
|
|
1948
|
+
if (typeof field.value !== "string") return void 0;
|
|
1949
|
+
fields.push({ name: field.name.trim(), value: field.value });
|
|
1950
|
+
}
|
|
1951
|
+
return { url: options.url.trim(), fields };
|
|
1952
|
+
}
|
|
1953
|
+
function urlencodeform(fields) {
|
|
1954
|
+
return fields.map((field) => `${formencode(field.name)}=${formencode(field.value)}`).join("&");
|
|
1955
|
+
}
|
|
1956
|
+
function formencode(value) {
|
|
1957
|
+
const bytes = [...new TextEncoder().encode(value)];
|
|
1958
|
+
return bytes.map((byte) => byte >= 65 && byte <= 90 || byte >= 97 && byte <= 122 || byte >= 48 && byte <= 57 || byte === 45 || byte === 95 || byte === 46 || byte === 126 ? String.fromCharCode(byte) : `%${byte.toString(16).toUpperCase().padStart(2, "0")}`).join("");
|
|
1959
|
+
}
|
|
1960
|
+
function multipartpayloadof(value) {
|
|
1961
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1962
|
+
const options = value;
|
|
1963
|
+
if (typeof options.url !== "string" || !options.url.trim()) return void 0;
|
|
1964
|
+
if (!Array.isArray(options.files) || options.files.length === 0) return void 0;
|
|
1965
|
+
const fields = [];
|
|
1966
|
+
for (const item of Array.isArray(options.fields) ? options.fields : []) {
|
|
1967
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return void 0;
|
|
1968
|
+
const field = item;
|
|
1969
|
+
if (typeof field.name !== "string" || !field.name.trim()) return void 0;
|
|
1970
|
+
if (typeof field.value !== "string") return void 0;
|
|
1971
|
+
fields.push({ name: field.name.trim(), value: field.value });
|
|
1972
|
+
}
|
|
1973
|
+
const files = [];
|
|
1974
|
+
for (const item of options.files) {
|
|
1975
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return void 0;
|
|
1976
|
+
const file = item;
|
|
1977
|
+
if (typeof file.name !== "string" || !file.name.trim()) return void 0;
|
|
1978
|
+
if (typeof file.filename !== "string" || !file.filename.trim()) return void 0;
|
|
1979
|
+
if (typeof file.mime !== "string" || !file.mime.trim()) return void 0;
|
|
1980
|
+
if (typeof file.content !== "string") return void 0;
|
|
1981
|
+
if (file.reviewed !== true) return void 0;
|
|
1982
|
+
files.push({ name: file.name.trim(), filename: file.filename.trim(), mime: file.mime.trim(), content: file.content, reviewed: true });
|
|
1983
|
+
}
|
|
1984
|
+
const payload = { url: options.url.trim(), fields, files, ...typeof options.boundary === "string" && options.boundary.trim() ? { boundary: options.boundary.trim() } : {} };
|
|
1985
|
+
return payload;
|
|
1986
|
+
}
|
|
1987
|
+
function newboundary() {
|
|
1988
|
+
return `----devthink${Math.random().toString(16).slice(2)}${Date.now().toString(16)}`;
|
|
1989
|
+
}
|
|
1990
|
+
function multipartchunks(payload) {
|
|
1991
|
+
const boundary = payload.boundary ?? newboundary();
|
|
1992
|
+
const chunks = [];
|
|
1993
|
+
for (const field of payload.fields) chunks.push(`--${boundary}\r
|
|
1994
|
+
content-disposition: form-data; name="${field.name}"\r
|
|
1995
|
+
\r
|
|
1996
|
+
${field.value}\r
|
|
1997
|
+
`);
|
|
1998
|
+
for (const file of payload.files) chunks.push(`--${boundary}\r
|
|
1999
|
+
content-disposition: form-data; name="${file.name}"; filename="${file.filename}"\r
|
|
2000
|
+
content-type: ${file.mime}\r
|
|
2001
|
+
\r
|
|
2002
|
+
${file.content}\r
|
|
2003
|
+
`);
|
|
2004
|
+
chunks.push(`--${boundary}--\r
|
|
2005
|
+
`);
|
|
2006
|
+
return { chunks, boundary, bytes: chunks.reduce((total, chunk) => total + chunk.length, 0) };
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
// netcontrol.ts
|
|
2010
|
+
var controlkinds = ["blockrequest", "mockresponse", "rewriteheaders", "setcookies", "readcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"];
|
|
2011
|
+
function patternorigin(pattern) {
|
|
2012
|
+
const trimmed = pattern.trim();
|
|
2013
|
+
if (!trimmed.startsWith("https://")) return void 0;
|
|
2014
|
+
const rest = trimmed.slice("https://".length);
|
|
2015
|
+
const host = rest.split("/")[0] ?? "";
|
|
2016
|
+
if (!host.trim()) return void 0;
|
|
2017
|
+
return `https://${host.toLowerCase()}`;
|
|
2018
|
+
}
|
|
2019
|
+
function matchurlpattern(pattern, url) {
|
|
2020
|
+
const origin = patternorigin(pattern);
|
|
2021
|
+
if (!origin) return false;
|
|
2022
|
+
let parsed;
|
|
2023
|
+
try {
|
|
2024
|
+
parsed = new URL(url);
|
|
2025
|
+
} catch {
|
|
2026
|
+
return false;
|
|
2027
|
+
}
|
|
2028
|
+
if (parsed.origin !== origin) return false;
|
|
2029
|
+
const patternpath = pattern.trim().slice(origin.length);
|
|
2030
|
+
if (patternpath === "" || patternpath === "/") return true;
|
|
2031
|
+
const segments = patternpath.split("/").filter((segment) => segment !== "");
|
|
2032
|
+
if (segments.includes("**")) return true;
|
|
2033
|
+
const pathsegments = parsed.pathname.split("/").filter((segment) => segment !== "");
|
|
2034
|
+
if (segments.length !== pathsegments.length) return false;
|
|
2035
|
+
return segments.every((segment, index) => segment === pathsegments[index] || segment.includes("*") && new RegExp(`^${segment.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*")}$`).test(pathsegments[index] ?? ""));
|
|
2036
|
+
}
|
|
2037
|
+
function blockruleof(value) {
|
|
2038
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2039
|
+
const options = value;
|
|
2040
|
+
if (typeof options.urlpattern !== "string" || !options.urlpattern.trim()) return void 0;
|
|
2041
|
+
const rule = { urlpattern: options.urlpattern.trim() };
|
|
2042
|
+
if (Array.isArray(options.resourcetypes)) {
|
|
2043
|
+
const types = options.resourcetypes.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
2044
|
+
if (types.length === 0) return void 0;
|
|
2045
|
+
rule.resourcetypes = types;
|
|
2046
|
+
}
|
|
2047
|
+
return rule;
|
|
2048
|
+
}
|
|
2049
|
+
function newblockrule(input) {
|
|
2050
|
+
return { id: input.id, runid: input.runid, stepid: input.stepid, urlpattern: input.urlpattern, ...input.resourcetypes !== void 0 ? { resourcetypes: input.resourcetypes } : {}, hits: 0, registeredat: input.at };
|
|
2051
|
+
}
|
|
2052
|
+
function mockspecof(value) {
|
|
2053
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2054
|
+
const options = value;
|
|
2055
|
+
if (typeof options.urlpattern !== "string" || !options.urlpattern.trim()) return void 0;
|
|
2056
|
+
if (typeof options.status !== "number" || !Number.isInteger(options.status) || options.status < 100 || options.status > 599) return void 0;
|
|
2057
|
+
const hasbody = typeof options.body === "string";
|
|
2058
|
+
const bodyref = typeof options.bodyref === "string" ? options.bodyref.trim() : "";
|
|
2059
|
+
if (!hasbody && bodyref === "") return void 0;
|
|
2060
|
+
const spec = { urlpattern: options.urlpattern.trim(), status: options.status };
|
|
2061
|
+
if (hasbody) spec.body = options.body;
|
|
2062
|
+
if (bodyref !== "") spec.bodyref = bodyref;
|
|
2063
|
+
if (options.headers && typeof options.headers === "object" && !Array.isArray(options.headers)) spec.headers = options.headers;
|
|
2064
|
+
if (options.reviewed === true) spec.reviewed = true;
|
|
2065
|
+
return spec;
|
|
2066
|
+
}
|
|
2067
|
+
function newmockspec(input) {
|
|
2068
|
+
return { id: input.id, runid: input.runid, stepid: input.stepid, urlpattern: input.urlpattern, status: input.status, ...input.headers !== void 0 ? { headers: input.headers } : {}, ...input.body !== void 0 ? { body: input.body } : {}, ...input.bodyref !== void 0 ? { bodyref: input.bodyref } : {}, reviewed: input.reviewed, hits: 0, registeredat: input.at };
|
|
2069
|
+
}
|
|
2070
|
+
function mockfor(url, specs) {
|
|
2071
|
+
return specs.find((spec) => spec.revertedat === void 0 && matchurlpattern(spec.urlpattern, url));
|
|
2072
|
+
}
|
|
2073
|
+
function headeruleof(value) {
|
|
2074
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2075
|
+
const options = value;
|
|
2076
|
+
if (typeof options.urlpattern !== "string" || !options.urlpattern.trim()) return void 0;
|
|
2077
|
+
if (typeof options.name !== "string" || !options.name.trim()) return void 0;
|
|
2078
|
+
if (options.operation !== "set" && options.operation !== "append" && options.operation !== "remove") return void 0;
|
|
2079
|
+
if (options.operation === "remove" && options.value !== void 0) return void 0;
|
|
2080
|
+
if (options.operation !== "remove" && typeof options.value !== "string") return void 0;
|
|
2081
|
+
const rule = { urlpattern: options.urlpattern.trim(), name: options.name.trim(), operation: options.operation };
|
|
2082
|
+
if (options.operation !== "remove") rule.value = typeof options.value === "string" ? options.value : "";
|
|
2083
|
+
return rule;
|
|
2084
|
+
}
|
|
2085
|
+
function newheaderule(input) {
|
|
2086
|
+
return { id: input.id, runid: input.runid, stepid: input.stepid, urlpattern: input.urlpattern, name: input.name, operation: input.operation, ...input.value !== void 0 ? { value: input.value } : {}, hits: 0, registeredat: input.at };
|
|
2087
|
+
}
|
|
2088
|
+
function applyheaderules(url, headers, rules) {
|
|
2089
|
+
const rewritten = { ...headers };
|
|
2090
|
+
const applied = [];
|
|
2091
|
+
for (const rule of rules) {
|
|
2092
|
+
if (rule.revertedat !== void 0) continue;
|
|
2093
|
+
if (!matchurlpattern(rule.urlpattern, url)) continue;
|
|
2094
|
+
const name = rule.name;
|
|
2095
|
+
if (rule.operation === "remove") {
|
|
2096
|
+
delete rewritten[name];
|
|
2097
|
+
applied.push(rule);
|
|
2098
|
+
continue;
|
|
2099
|
+
}
|
|
2100
|
+
const value = rule.value ?? "";
|
|
2101
|
+
if (rule.operation === "set") rewritten[name] = value;
|
|
2102
|
+
else rewritten[name] = rewritten[name] !== void 0 ? `${rewritten[name]}, ${value}` : value;
|
|
2103
|
+
applied.push(rule);
|
|
2104
|
+
}
|
|
2105
|
+
return { headers: rewritten, applied };
|
|
2106
|
+
}
|
|
2107
|
+
function revertrule(rule, at) {
|
|
2108
|
+
if (rule.revertedat !== void 0) return rule;
|
|
2109
|
+
return { ...rule, revertedat: at };
|
|
2110
|
+
}
|
|
2111
|
+
function cookierecordof(value) {
|
|
2112
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2113
|
+
const options = value;
|
|
2114
|
+
if (typeof options.name !== "string" || !options.name.trim()) return void 0;
|
|
2115
|
+
if (typeof options.domain !== "string" || !options.domain.trim()) return void 0;
|
|
2116
|
+
if (typeof options.path !== "string" || !options.path.trim()) return void 0;
|
|
2117
|
+
if (typeof options.value !== "string") return void 0;
|
|
2118
|
+
const record2 = { name: options.name.trim(), domain: options.domain.trim().toLowerCase(), path: options.path.trim(), value: options.value };
|
|
2119
|
+
if (typeof options.expiresat === "number" && Number.isFinite(options.expiresat)) record2.expiresat = options.expiresat;
|
|
2120
|
+
return record2;
|
|
2121
|
+
}
|
|
2122
|
+
function cookiedomaingranted(domain, grants) {
|
|
2123
|
+
const host = domain.trim().toLowerCase().replace(/^\./, "");
|
|
2124
|
+
return grants.some((grant) => {
|
|
2125
|
+
let granthost = "";
|
|
2126
|
+
try {
|
|
2127
|
+
granthost = new URL(grant).hostname.toLowerCase();
|
|
2128
|
+
} catch {
|
|
2129
|
+
return false;
|
|
2130
|
+
}
|
|
2131
|
+
return host === granthost || host.endsWith(`.${granthost}`);
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
function redactedcookies(records) {
|
|
2135
|
+
return records.map((record2) => ({ name: record2.name, domain: record2.domain, path: record2.path, ...record2.expiresat !== void 0 ? { expiresat: record2.expiresat } : {} }));
|
|
2136
|
+
}
|
|
2137
|
+
function proxyrouteof(value) {
|
|
2138
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2139
|
+
const options = value;
|
|
2140
|
+
if (options.scheme !== "http" && options.scheme !== "https" && options.scheme !== "socks4" && options.scheme !== "socks5") return void 0;
|
|
2141
|
+
if (typeof options.host !== "string" || !options.host.trim()) return void 0;
|
|
2142
|
+
if (typeof options.port !== "number" || !Number.isInteger(options.port) || options.port < 1 || options.port > 65535) return void 0;
|
|
2143
|
+
if (!Array.isArray(options.bypass) || options.bypass.length === 0 || !options.bypass.every((item) => typeof item === "string" && item.trim().length > 0)) return void 0;
|
|
2144
|
+
return { scheme: options.scheme, host: options.host.trim(), port: options.port, bypass: options.bypass.map((item) => item.trim()) };
|
|
2145
|
+
}
|
|
2146
|
+
function ratelimitreadof(headers, origin, now) {
|
|
2147
|
+
const pick = (name) => {
|
|
2148
|
+
for (const key of Object.keys(headers)) {
|
|
2149
|
+
if (key.toLowerCase() !== name) continue;
|
|
2150
|
+
const value = Number(headers[key]);
|
|
2151
|
+
return Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
2152
|
+
}
|
|
2153
|
+
return void 0;
|
|
2154
|
+
};
|
|
2155
|
+
const remaining = pick("x-ratelimit-remaining");
|
|
2156
|
+
const limit = pick("x-ratelimit-limit");
|
|
2157
|
+
const reset = pick("x-ratelimit-reset");
|
|
2158
|
+
if (remaining === void 0 && limit === void 0 && reset === void 0) return void 0;
|
|
2159
|
+
const read = { origin, ...remaining !== void 0 ? { remaining } : {}, ...limit !== void 0 ? { limit } : {}, resetat: now, at: now };
|
|
2160
|
+
if (reset !== void 0) read.resetat = reset > Math.floor(now / 1e3) ? reset * 1e3 : now + reset * 1e3;
|
|
2161
|
+
return read;
|
|
2162
|
+
}
|
|
2163
|
+
function retryafterof(status, headers) {
|
|
2164
|
+
if (status !== 429 && status !== 503) return void 0;
|
|
2165
|
+
for (const key of Object.keys(headers)) {
|
|
2166
|
+
if (key.toLowerCase() !== "retry-after") continue;
|
|
2167
|
+
const raw = headers[key];
|
|
2168
|
+
if (raw === void 0) continue;
|
|
2169
|
+
const value = raw.trim();
|
|
2170
|
+
const seconds = Number(value);
|
|
2171
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
2172
|
+
const date = Date.parse(value);
|
|
2173
|
+
if (Number.isFinite(date)) return Math.max(0, date - Date.now());
|
|
2174
|
+
return void 0;
|
|
2175
|
+
}
|
|
2176
|
+
return void 0;
|
|
2177
|
+
}
|
|
2178
|
+
function ratelimitwait(state, now) {
|
|
2179
|
+
if (!state) return 0;
|
|
2180
|
+
return Math.max(0, state.resetat - now);
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
// netwatch.ts
|
|
2184
|
+
var netwatchkinds = ["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"];
|
|
2185
|
+
function resourcefacts(entries) {
|
|
2186
|
+
const facts = [];
|
|
2187
|
+
for (const entry of entries) {
|
|
2188
|
+
const url = typeof entry.name === "string" ? entry.name : "";
|
|
2189
|
+
if (!url) continue;
|
|
2190
|
+
const entrytype = typeof entry.entryType === "string" ? entry.entryType : "resource";
|
|
2191
|
+
if (entrytype !== "resource" && entrytype !== "navigation") continue;
|
|
2192
|
+
facts.push({
|
|
2193
|
+
url,
|
|
2194
|
+
initiator: typeof entry.initiatorType === "string" ? entry.initiatorType : "",
|
|
2195
|
+
entrytype,
|
|
2196
|
+
start: typeof entry.startTime === "number" && Number.isFinite(entry.startTime) ? entry.startTime : 0,
|
|
2197
|
+
duration: typeof entry.duration === "number" && Number.isFinite(entry.duration) ? entry.duration : 0,
|
|
2198
|
+
transfer: typeof entry.transferSize === "number" && Number.isFinite(entry.transferSize) ? entry.transferSize : 0,
|
|
2199
|
+
protocol: typeof entry.nextHopProtocol === "string" ? entry.nextHopProtocol : "",
|
|
2200
|
+
...typeof entry.responseStatus === "number" && Number.isInteger(entry.responseStatus) ? { status: entry.responseStatus } : {},
|
|
2201
|
+
...entry.failed === true ? { failed: true } : {}
|
|
2202
|
+
});
|
|
2203
|
+
}
|
|
2204
|
+
return facts;
|
|
2205
|
+
}
|
|
2206
|
+
function failureclass(fact) {
|
|
2207
|
+
if (fact.status !== void 0 && fact.status >= 400) return { errorclass: "httperror", status: fact.status };
|
|
2208
|
+
if (fact.failed === true) return { errorclass: "networkerror", status: 0 };
|
|
2209
|
+
if ((fact.initiator === "fetch" || fact.initiator === "xmlhttprequest") && fact.duration > 0 && fact.transfer === 0 && fact.protocol === "") return { errorclass: "networkerror", status: 0 };
|
|
2210
|
+
return { status: fact.status ?? 0 };
|
|
2211
|
+
}
|
|
2212
|
+
function correlationid(runid, index) {
|
|
2213
|
+
return `${runid}-${index + 1}`;
|
|
2214
|
+
}
|
|
2215
|
+
function newexchange(input) {
|
|
2216
|
+
const verdict = failureclass(input.fact);
|
|
2217
|
+
let origin = "";
|
|
2218
|
+
try {
|
|
2219
|
+
origin = new URL(input.fact.url).origin;
|
|
2220
|
+
} catch {
|
|
2221
|
+
origin = "";
|
|
2222
|
+
}
|
|
2223
|
+
const method = input.fact.initiator === "fetch" || input.fact.initiator === "xmlhttprequest" ? "?" : "GET";
|
|
2224
|
+
const statusclass = verdict.status >= 100 && verdict.status < 600 ? verdict.status >= 200 && verdict.status < 300 ? "success" : verdict.status >= 300 && verdict.status < 400 ? "redirect" : verdict.status >= 400 && verdict.status < 500 ? "clienterror" : verdict.status >= 500 ? "servererror" : "informational" : "unknown";
|
|
2225
|
+
return { id: input.id, runid: input.runid, stepid: input.stepid, correlationid: input.correlationid, url: input.fact.url, origin, method, status: verdict.status, statusclass, ...verdict.errorclass !== void 0 ? { errorclass: verdict.errorclass } : {}, source: "page", ...input.fact.initiator ? { initiator: input.fact.initiator } : {}, timing: Math.round(input.fact.duration), bytes: input.fact.transfer, at: input.at };
|
|
2226
|
+
}
|
|
2227
|
+
function pairexchange(exchange, response) {
|
|
2228
|
+
if (exchange.correlationid !== response.correlationid) throw new Error(`The response ${response.correlationid} does not pair with the exchange ${exchange.correlationid}.`);
|
|
2229
|
+
return { ...exchange, status: response.status, statusclass: response.status >= 200 && response.status < 300 ? "success" : response.status >= 400 && response.status < 500 ? "clienterror" : response.status >= 500 ? "servererror" : response.status >= 300 && response.status < 400 ? "redirect" : "unknown", bytes: response.bytes, ...response.mime !== void 0 ? { mime: response.mime } : {}, ...response.bodyref !== void 0 ? { bodyref: response.bodyref } : {}, ...Object.keys(response.headers).length > 0 ? { responseheaders: response.headers } : {} };
|
|
2230
|
+
}
|
|
2231
|
+
function filterexchanges(exchanges, filter) {
|
|
2232
|
+
return exchanges.filter((exchange) => (filter.runid === void 0 || exchange.runid === filter.runid) && (filter.origin === void 0 || exchange.origin === filter.origin) && (filter.status === void 0 || (filter.status === "failed" ? exchange.errorclass !== void 0 : exchange.status === filter.status)));
|
|
2233
|
+
}
|
|
2234
|
+
function headerfilterof(value) {
|
|
2235
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { allow: [], redact: [] };
|
|
2236
|
+
const entry = value;
|
|
2237
|
+
const names = (source) => Array.isArray(source) ? source.filter((name) => typeof name === "string" && name.trim().length > 0).map((name) => name.trim().toLowerCase()) : [];
|
|
2238
|
+
return { allow: names(entry.allow), redact: names(entry.redact) };
|
|
2239
|
+
}
|
|
2240
|
+
function capturedheaders(headers, filter) {
|
|
2241
|
+
const result = {};
|
|
2242
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
2243
|
+
const key = name.trim().toLowerCase();
|
|
2244
|
+
if (filter.allow.length > 0 && !filter.allow.includes(key)) continue;
|
|
2245
|
+
result[key] = filter.redact.includes(key) ? "[redacted]" : value;
|
|
2246
|
+
}
|
|
2247
|
+
return result;
|
|
2248
|
+
}
|
|
2249
|
+
function bodyfilterof(value) {
|
|
2250
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
2251
|
+
const entry = value;
|
|
2252
|
+
const filter = {};
|
|
2253
|
+
if (typeof entry.urlpattern === "string" && entry.urlpattern.trim()) filter.urlpattern = entry.urlpattern.trim();
|
|
2254
|
+
if (Array.isArray(entry.mimes)) filter.mimes = entry.mimes.filter((mime) => typeof mime === "string" && mime.trim().length > 0).map((mime) => mime.trim().toLowerCase());
|
|
2255
|
+
if (typeof entry.ceiling === "number" && Number.isFinite(entry.ceiling) && entry.ceiling >= 0) filter.ceiling = entry.ceiling;
|
|
2256
|
+
return filter;
|
|
2257
|
+
}
|
|
2258
|
+
function bodymatches(filter, exchange) {
|
|
2259
|
+
if (filter.urlpattern !== void 0 && !exchange.url.includes(filter.urlpattern)) return false;
|
|
2260
|
+
if (filter.mimes !== void 0 && filter.mimes.length > 0) {
|
|
2261
|
+
const mime = ((exchange.mime ?? "").split(";")[0] ?? "").trim().toLowerCase();
|
|
2262
|
+
if (!filter.mimes.includes(mime)) return false;
|
|
2263
|
+
}
|
|
2264
|
+
return true;
|
|
2265
|
+
}
|
|
2266
|
+
var privatemimes = /* @__PURE__ */ new Set(["text/html", "text/plain", "text/xml", "application/xml", "application/json", "text/json", "application/x-www-form-urlencoded", "application/graphql", "multipart/form-data"]);
|
|
2267
|
+
function privatemime(mime) {
|
|
2268
|
+
return privatemimes.has((mime.split(";")[0] ?? "").trim().toLowerCase());
|
|
2269
|
+
}
|
|
2270
|
+
function capturebody(input) {
|
|
2271
|
+
if (!bodymatches(input.filter, input.exchange)) return { refused: `The exchange ${input.exchange.correlationid} does not match the reviewed body filter.` };
|
|
2272
|
+
const ceiling = input.filter.ceiling;
|
|
2273
|
+
const stored = ceiling !== void 0 && input.body.length > ceiling ? input.body.slice(0, ceiling) : input.body;
|
|
2274
|
+
return { record: { ref: input.ref, runid: input.runid, correlationid: input.exchange.correlationid, url: input.exchange.url, mime: input.mime, bytes: stored.length, body: stored, at: input.at }, truncated: stored.length < input.body.length };
|
|
2275
|
+
}
|
|
2276
|
+
function payloadshapeof(body) {
|
|
2277
|
+
if (body === void 0) return [];
|
|
2278
|
+
try {
|
|
2279
|
+
const parsed = JSON.parse(body);
|
|
2280
|
+
const shape = (value) => value && typeof value === "object" && !Array.isArray(value) ? Object.keys(value) : [];
|
|
2281
|
+
if (Array.isArray(parsed)) return parsed.length > 0 ? shape(parsed[0]) : [];
|
|
2282
|
+
return shape(parsed);
|
|
2283
|
+
} catch {
|
|
2284
|
+
return [];
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
function isapicandidate(exchange) {
|
|
2288
|
+
if (exchange.initiator === "fetch" || exchange.initiator === "xmlhttprequest") return true;
|
|
2289
|
+
if (exchange.bodyref !== void 0) return true;
|
|
2290
|
+
try {
|
|
2291
|
+
return /\/api\/|\/graphql|\.json($|\?)|\/v\d+\//i.test(new URL(exchange.url).pathname);
|
|
2292
|
+
} catch {
|
|
2293
|
+
return false;
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
function apientries(exchanges, bodies) {
|
|
2297
|
+
const bodybyref = new Map(bodies.map((body) => [body.correlationid, body]));
|
|
2298
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2299
|
+
for (const exchange of exchanges) {
|
|
2300
|
+
if (!isapicandidate(exchange)) continue;
|
|
2301
|
+
let endpoint = exchange.url;
|
|
2302
|
+
let origin = exchange.origin;
|
|
2303
|
+
try {
|
|
2304
|
+
const parsed = new URL(exchange.url);
|
|
2305
|
+
endpoint = `${parsed.origin}${parsed.pathname}`;
|
|
2306
|
+
origin = parsed.origin;
|
|
2307
|
+
} catch {
|
|
2308
|
+
}
|
|
2309
|
+
const key = `${exchange.method} ${endpoint}`;
|
|
2310
|
+
const group = groups.get(key) ?? { endpoint, method: exchange.method, origin, mimes: /* @__PURE__ */ new Map(), frequency: 0, json: 0, captured: 0, shapes: /* @__PURE__ */ new Map(), correlationids: [] };
|
|
2311
|
+
group.frequency += 1;
|
|
2312
|
+
group.correlationids.push(exchange.correlationid);
|
|
2313
|
+
const body = exchange.bodyref !== void 0 ? bodybyref.get(exchange.correlationid) : void 0;
|
|
2314
|
+
const mime = body?.mime ?? exchange.mime ?? "";
|
|
2315
|
+
group.mimes.set(mime, (group.mimes.get(mime) ?? 0) + 1);
|
|
2316
|
+
if (body !== void 0) {
|
|
2317
|
+
group.captured += 1;
|
|
2318
|
+
const shape = payloadshapeof(body.body);
|
|
2319
|
+
if (shape.length > 0) group.json += 1;
|
|
2320
|
+
const shapekey = shape.join(",");
|
|
2321
|
+
group.shapes.set(shapekey, (group.shapes.get(shapekey) ?? 0) + 1);
|
|
2322
|
+
}
|
|
2323
|
+
groups.set(key, group);
|
|
2324
|
+
}
|
|
2325
|
+
return [...groups.values()].map((group) => {
|
|
2326
|
+
const mime = [...group.mimes.entries()].sort((left, right) => right[1] - left[1])[0]?.[0] ?? "";
|
|
2327
|
+
const modalshape = [...group.shapes.entries()].sort((left, right) => right[1] - left[1])[0];
|
|
2328
|
+
return { endpoint: group.endpoint, method: group.method, mime, frequency: group.frequency, payloadshape: (modalshape?.[0] ?? "").split(",").filter(Boolean), jsonshare: group.captured > 0 ? group.json / group.captured : 0, stability: group.captured > 0 ? (modalshape?.[1] ?? 0) / group.captured : 0, origin: group.origin, correlationids: group.correlationids };
|
|
2329
|
+
});
|
|
2330
|
+
}
|
|
2331
|
+
function rankapis(entries) {
|
|
2332
|
+
const score = (entry) => entry.frequency * (1 + entry.jsonshare + entry.stability);
|
|
2333
|
+
return [...entries].sort((left, right) => score(right) - score(left) || right.frequency - left.frequency);
|
|
2334
|
+
}
|
|
2335
|
+
function apireplayspecof(value) {
|
|
2336
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2337
|
+
const entry = value;
|
|
2338
|
+
if (typeof entry.endpoint !== "string" || !entry.endpoint.trim()) return void 0;
|
|
2339
|
+
const spec = { endpoint: entry.endpoint.trim() };
|
|
2340
|
+
if (typeof entry.verb === "string" && entry.verb.trim()) spec.verb = entry.verb.trim().toUpperCase();
|
|
2341
|
+
if (entry.overrides !== void 0 && entry.overrides !== null && typeof entry.overrides === "object" && !Array.isArray(entry.overrides)) {
|
|
2342
|
+
const overrides = {};
|
|
2343
|
+
for (const [name, override] of Object.entries(entry.overrides)) {
|
|
2344
|
+
if (typeof override === "string") overrides[name] = override;
|
|
2345
|
+
}
|
|
2346
|
+
spec.overrides = overrides;
|
|
2347
|
+
}
|
|
2348
|
+
if (Array.isArray(entry.paths)) spec.paths = entry.paths.filter((path) => typeof path === "string" && path.trim().length > 0);
|
|
2349
|
+
return spec;
|
|
2350
|
+
}
|
|
2351
|
+
function replayurl(spec) {
|
|
2352
|
+
const url = new URL(spec.endpoint);
|
|
2353
|
+
for (const [name, value] of Object.entries(spec.overrides ?? {})) url.searchParams.set(name, value);
|
|
2354
|
+
return url.toString();
|
|
2355
|
+
}
|
|
2356
|
+
function extractvalues(body, paths) {
|
|
2357
|
+
let parsed;
|
|
2358
|
+
try {
|
|
2359
|
+
parsed = JSON.parse(body);
|
|
2360
|
+
} catch {
|
|
2361
|
+
return paths.map((path) => ({ path, missing: true }));
|
|
2362
|
+
}
|
|
2363
|
+
const fields = readpath(parsed, paths.map((path) => ({ name: path, path, kind: "json" })));
|
|
2364
|
+
return fields.map((field) => ({ path: field.path, ...field.value !== void 0 ? { value: field.value } : {}, ...field.missing ? { missing: true } : {} }));
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
// socketbus.ts
|
|
2368
|
+
var socketkinds = ["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"];
|
|
2369
|
+
function channelorigin(url) {
|
|
2370
|
+
try {
|
|
2371
|
+
const parsed = new URL(url);
|
|
2372
|
+
const protocol = parsed.protocol === "wss:" ? "https:" : parsed.protocol === "ws:" ? "http:" : parsed.protocol;
|
|
2373
|
+
return `${protocol}//${parsed.host}`;
|
|
2374
|
+
} catch {
|
|
2375
|
+
return "";
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
function channeloptionsof(value) {
|
|
2379
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2380
|
+
const entry = value;
|
|
2381
|
+
if (typeof entry.url !== "string" || !entry.url.trim()) return void 0;
|
|
2382
|
+
const options = {};
|
|
2383
|
+
if (Array.isArray(entry.protocols)) options.protocols = entry.protocols.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
2384
|
+
if (typeof entry.reconnect === "number" && Number.isFinite(entry.reconnect)) options.reconnect = entry.reconnect;
|
|
2385
|
+
if (typeof entry.backoff === "number" && Number.isFinite(entry.backoff)) options.backoff = entry.backoff;
|
|
2386
|
+
if (typeof entry.backoffceiling === "number" && Number.isFinite(entry.backoffceiling)) options.backoffceiling = entry.backoffceiling;
|
|
2387
|
+
if (typeof entry.lifetime === "number" && Number.isFinite(entry.lifetime)) options.lifetime = entry.lifetime;
|
|
2388
|
+
return { url: entry.url.trim(), options };
|
|
2389
|
+
}
|
|
2390
|
+
function newchannel(input) {
|
|
2391
|
+
return { id: input.id, runid: input.runid, stepid: input.stepid, kind: input.kind, url: input.url, origin: channelorigin(input.url), state: "connecting", openedat: input.at, sent: 0, received: 0, reconnects: 0, ...input.protocols !== void 0 && input.protocols.length > 0 ? { protocols: [...input.protocols] } : {} };
|
|
2392
|
+
}
|
|
2393
|
+
function reconnectwaits(attempts, base, ceiling) {
|
|
2394
|
+
const count = Math.max(0, Math.floor(attempts));
|
|
2395
|
+
const waits = [];
|
|
2396
|
+
let wait = Math.max(0, base);
|
|
2397
|
+
for (let index = 0; index < count; index += 1) {
|
|
2398
|
+
waits.push(wait);
|
|
2399
|
+
const next = wait * 2;
|
|
2400
|
+
wait = ceiling !== void 0 && Number.isFinite(ceiling) && ceiling >= 0 ? Math.min(next, ceiling) : next;
|
|
2401
|
+
}
|
|
2402
|
+
return waits;
|
|
2403
|
+
}
|
|
2404
|
+
async function openchannel(input) {
|
|
2405
|
+
const sleep = input.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, Math.max(0, milliseconds))));
|
|
2406
|
+
const now = input.now ?? Date.now;
|
|
2407
|
+
const attempts = Math.max(1, Math.floor(input.options.reconnect ?? 0) + 1);
|
|
2408
|
+
const waits = reconnectwaits(attempts - 1, input.options.backoff ?? 0, input.options.backoffceiling);
|
|
2409
|
+
let record2 = { ...input.record, state: "connecting" };
|
|
2410
|
+
let lasterror = "";
|
|
2411
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
2412
|
+
try {
|
|
2413
|
+
const result = await input.connect(record2.url, record2.protocols ?? []);
|
|
2414
|
+
if (result.open) return { ...record2, state: "open", openedat: now() };
|
|
2415
|
+
lasterror = result.error ?? `closed with code ${result.code ?? 0}`;
|
|
2416
|
+
} catch (error) {
|
|
2417
|
+
lasterror = error instanceof Error ? error.message : String(error);
|
|
2418
|
+
}
|
|
2419
|
+
if (attempt < attempts - 1) {
|
|
2420
|
+
const wait = waits[attempt] ?? 0;
|
|
2421
|
+
if (wait > 0) await sleep(wait);
|
|
2422
|
+
record2 = { ...record2, reconnects: record2.reconnects + 1 };
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
return { ...record2, state: "failed", error: lasterror };
|
|
2426
|
+
}
|
|
2427
|
+
function closechannel(record2, at, error) {
|
|
2428
|
+
const state = error !== void 0 ? "failed" : "closed";
|
|
2429
|
+
return { ...record2, state, closedat: at, ...error !== void 0 ? { error } : {} };
|
|
2430
|
+
}
|
|
2431
|
+
function tagmessage(state, channelid, stream, payload, at) {
|
|
2432
|
+
const sequence = (state.sequences[channelid] ?? 0) + 1;
|
|
2433
|
+
const envelope = { channelid, stream, payload, sequence, at };
|
|
2434
|
+
return { state: { sequences: { ...state.sequences, [channelid]: sequence }, queue: state.queue }, envelope };
|
|
2435
|
+
}
|
|
2436
|
+
function publishmessage(state, channelid, stream, payload, at) {
|
|
2437
|
+
return tagmessage(state, channelid, stream, payload, at);
|
|
2438
|
+
}
|
|
2439
|
+
function receivemessage(state, channelid, stream, payload, at) {
|
|
2440
|
+
const tagged = tagmessage(state, channelid, stream, payload, at);
|
|
2441
|
+
return { state: { ...tagged.state, queue: [...state.queue, tagged.envelope] }, envelope: tagged.envelope };
|
|
2442
|
+
}
|
|
2443
|
+
function pathstep2(current, segment) {
|
|
2444
|
+
if (Array.isArray(current) && /^\d+$/.test(segment)) return current[Number.parseInt(segment, 10)];
|
|
2445
|
+
if (current && typeof current === "object" && !Array.isArray(current)) return current[segment];
|
|
2446
|
+
return void 0;
|
|
2447
|
+
}
|
|
2448
|
+
function matchmessage(filter, envelope) {
|
|
2449
|
+
if (!filter) return true;
|
|
2450
|
+
if (filter.stream !== void 0 && filter.stream !== envelope.stream) return false;
|
|
2451
|
+
if (filter.path !== void 0) {
|
|
2452
|
+
try {
|
|
2453
|
+
const parsed = JSON.parse(envelope.payload);
|
|
2454
|
+
let current = parsed;
|
|
2455
|
+
let missing = false;
|
|
2456
|
+
for (const segment of filter.path.split(".")) {
|
|
2457
|
+
const next = pathstep2(current, segment);
|
|
2458
|
+
if (next === void 0) {
|
|
2459
|
+
missing = true;
|
|
2460
|
+
break;
|
|
2461
|
+
}
|
|
2462
|
+
current = next;
|
|
2463
|
+
}
|
|
2464
|
+
if (missing) return false;
|
|
2465
|
+
} catch {
|
|
2466
|
+
return false;
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
return true;
|
|
2470
|
+
}
|
|
2471
|
+
function collectmessages(state, channelid, filter) {
|
|
2472
|
+
const limit = filter?.limit !== void 0 && Number.isFinite(filter.limit) && filter.limit >= 1 ? Math.floor(filter.limit) : Number.POSITIVE_INFINITY;
|
|
2473
|
+
const matched = [];
|
|
2474
|
+
const queue = [];
|
|
2475
|
+
for (const envelope of state.queue) {
|
|
2476
|
+
if (envelope.channelid === channelid && matched.length < limit && matchmessage(filter, envelope)) matched.push(envelope);
|
|
2477
|
+
else queue.push(envelope);
|
|
2478
|
+
}
|
|
2479
|
+
return { state: { sequences: state.sequences, queue }, matched };
|
|
2480
|
+
}
|
|
2481
|
+
function sequenceintegrity(envelopes) {
|
|
2482
|
+
const last = /* @__PURE__ */ new Map();
|
|
2483
|
+
const gaps = [];
|
|
2484
|
+
for (const envelope of envelopes) {
|
|
2485
|
+
const expected = (last.get(envelope.channelid) ?? 0) + 1;
|
|
2486
|
+
if (envelope.sequence !== expected) gaps.push({ channelid: envelope.channelid, expected, found: envelope.sequence });
|
|
2487
|
+
last.set(envelope.channelid, Math.max(envelope.sequence, expected));
|
|
2488
|
+
}
|
|
2489
|
+
return { ok: gaps.length === 0, gaps };
|
|
2490
|
+
}
|
|
2491
|
+
function messagefilterof(value) {
|
|
2492
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
2493
|
+
const entry = value;
|
|
2494
|
+
const filter = {};
|
|
2495
|
+
if (typeof entry.stream === "string" && entry.stream.trim()) filter.stream = entry.stream.trim();
|
|
2496
|
+
if (typeof entry.path === "string" && entry.path.trim()) filter.path = entry.path.trim();
|
|
2497
|
+
if (typeof entry.limit === "number" && Number.isFinite(entry.limit) && entry.limit >= 1) filter.limit = Math.floor(entry.limit);
|
|
2498
|
+
return filter;
|
|
2499
|
+
}
|
|
2500
|
+
function parsessetext(text2) {
|
|
2501
|
+
const separator = text2.lastIndexOf("\n\n");
|
|
2502
|
+
const complete = separator === -1 ? "" : text2.slice(0, separator + 2);
|
|
2503
|
+
const rest = separator === -1 ? text2 : text2.slice(separator + 2);
|
|
2504
|
+
const events = [];
|
|
2505
|
+
for (const block of complete.split(/\n\n/)) {
|
|
2506
|
+
const id = [];
|
|
2507
|
+
const names = [];
|
|
2508
|
+
const data = [];
|
|
2509
|
+
let retry;
|
|
2510
|
+
for (const line of block.split("\n")) {
|
|
2511
|
+
if (line === "" || line.startsWith(":")) continue;
|
|
2512
|
+
const colon = line.indexOf(":");
|
|
2513
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
2514
|
+
let value = colon === -1 ? "" : line.slice(colon + 1);
|
|
2515
|
+
if (value.startsWith(" ")) value = value.slice(1);
|
|
2516
|
+
if (field === "id" && value !== "") id.push(value);
|
|
2517
|
+
if (field === "event" && value !== "") names.push(value);
|
|
2518
|
+
if (field === "data") data.push(value);
|
|
2519
|
+
if (field === "retry" && /^\d+$/.test(value)) retry = Number.parseInt(value, 10);
|
|
2520
|
+
}
|
|
2521
|
+
if (id.length === 0 && names.length === 0 && data.length === 0) continue;
|
|
2522
|
+
events.push({ ...id.length > 0 ? { id: id[id.length - 1] } : {}, ...names.length > 0 ? { event: names[names.length - 1] } : {}, data: data.join("\n"), ...retry !== void 0 ? { retry } : {} });
|
|
2523
|
+
}
|
|
2524
|
+
return { events, rest };
|
|
2525
|
+
}
|
|
2526
|
+
function sserequestheaders(record2) {
|
|
2527
|
+
return { accept: "text/event-stream", ...record2.lasteventid !== void 0 && record2.lasteventid !== "" ? { "last-event-id": record2.lasteventid } : {} };
|
|
2528
|
+
}
|
|
2529
|
+
function subscriptionoptionsof(value) {
|
|
2530
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2531
|
+
const entry = value;
|
|
2532
|
+
if (typeof entry.url !== "string" || !entry.url.trim()) return void 0;
|
|
2533
|
+
const cancel = entry.cancel;
|
|
2534
|
+
if (!cancel || typeof cancel !== "object" || Array.isArray(cancel)) return void 0;
|
|
2535
|
+
const cancelrecord = cancel;
|
|
2536
|
+
if (cancelrecord.kind !== "stop" && cancelrecord.kind !== "lifetime") return void 0;
|
|
2537
|
+
if (typeof cancelrecord.value !== "string" && typeof cancelrecord.value !== "number") return void 0;
|
|
2538
|
+
const result = { url: entry.url.trim(), cancel: { kind: cancelrecord.kind, value: cancelrecord.value } };
|
|
2539
|
+
if (typeof entry.lifetime === "number" && Number.isFinite(entry.lifetime) && entry.lifetime > 0) result.lifetime = entry.lifetime;
|
|
2540
|
+
if (typeof entry.lasteventid === "string" && entry.lasteventid.trim()) result.lasteventid = entry.lasteventid.trim();
|
|
2541
|
+
return result;
|
|
2542
|
+
}
|
|
2543
|
+
function pollcursorof(value) {
|
|
2544
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2545
|
+
const entry = value;
|
|
2546
|
+
if (typeof entry.url !== "string" || !entry.url.trim()) return void 0;
|
|
2547
|
+
if (typeof entry.cursorfield !== "string" || !entry.cursorfield.trim()) return void 0;
|
|
2548
|
+
if (typeof entry.interval !== "number" || !Number.isFinite(entry.interval) || entry.interval <= 0) return void 0;
|
|
2549
|
+
const stop = entry.stop;
|
|
2550
|
+
if (!stop || typeof stop !== "object" || Array.isArray(stop)) return void 0;
|
|
2551
|
+
const stoprecord = stop;
|
|
2552
|
+
if (typeof stoprecord.field !== "string" || !stoprecord.field.trim()) return void 0;
|
|
2553
|
+
if (typeof stoprecord.equals !== "string") return void 0;
|
|
2554
|
+
const cursor = { url: entry.url.trim(), cursorfield: entry.cursorfield.trim(), interval: entry.interval, stop: { field: stoprecord.field.trim(), equals: stoprecord.equals } };
|
|
2555
|
+
if (typeof entry.maxpolls === "number" && Number.isFinite(entry.maxpolls) && entry.maxpolls >= 1) cursor.maxpolls = Math.floor(entry.maxpolls);
|
|
2556
|
+
if (typeof entry.param === "string" && entry.param.trim()) cursor.param = entry.param.trim();
|
|
2557
|
+
return cursor;
|
|
2558
|
+
}
|
|
2559
|
+
function cursorfrom(response, field) {
|
|
2560
|
+
let current = response;
|
|
2561
|
+
for (const segment of field.split(".")) {
|
|
2562
|
+
const next = pathstep2(current, segment);
|
|
2563
|
+
if (next === void 0) return void 0;
|
|
2564
|
+
current = next;
|
|
2565
|
+
}
|
|
2566
|
+
return current === void 0 || current === null ? void 0 : String(current);
|
|
2567
|
+
}
|
|
2568
|
+
function pollurl(cursor, value) {
|
|
2569
|
+
if (cursor.param === void 0 || value === void 0) {
|
|
2570
|
+
return { url: cursor.url, ...value !== void 0 ? { body: JSON.stringify({ [cursor.cursorfield]: value }) } : {} };
|
|
2571
|
+
}
|
|
2572
|
+
const url = new URL(cursor.url);
|
|
2573
|
+
url.searchParams.set(cursor.param, value);
|
|
2574
|
+
return { url: url.toString() };
|
|
2575
|
+
}
|
|
2576
|
+
function polldecision(input) {
|
|
2577
|
+
if (input.cancelled?.() === true) return { continue: false, reason: "The long poll loop was cancelled." };
|
|
2578
|
+
if (input.expiresat !== void 0 && input.now >= input.expiresat) return { continue: false, reason: "The long poll loop stopped at the reviewed plan expiry." };
|
|
2579
|
+
const stopvalue = cursorfrom(input.response, input.cursor.stop.field);
|
|
2580
|
+
if (stopvalue !== void 0 && stopvalue === input.cursor.stop.equals) return { continue: false, reason: `The stop condition matched ${input.cursor.stop.field} ${stopvalue}.` };
|
|
2581
|
+
if (input.cursor.maxpolls !== void 0 && input.polls + 1 >= input.cursor.maxpolls) return { continue: false, reason: `The long poll loop reached the reviewed poll ceiling of ${input.cursor.maxpolls}.` };
|
|
2582
|
+
const value = cursorfrom(input.response, input.cursor.cursorfield);
|
|
2583
|
+
const next = pollurl(input.cursor, value);
|
|
2584
|
+
return { continue: true, reason: "The long poll loop continues.", ...value !== void 0 ? { cursor: value } : {}, next: { ...next, wait: input.cursor.interval } };
|
|
2585
|
+
}
|
|
2586
|
+
|
|
1713
2587
|
// policy.ts
|
|
1714
|
-
var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql"]);
|
|
1715
|
-
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction"]);
|
|
1716
|
-
var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml"]);
|
|
2588
|
+
var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"]);
|
|
2589
|
+
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies"]);
|
|
2590
|
+
var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies"]);
|
|
1717
2591
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
1718
2592
|
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
1719
2593
|
var targetactions = /* @__PURE__ */ new Set(["inspect", "focus", "click", "type", "scroll", "select", "hover", "clickdeep", "rightclick", "doubleclick", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "highlight", "setattribute", "removeattribute", "waitfor", "shiftclick", "typetime", "appendtext", "setvalue", "typeedit", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "expanddetails", "verifyvisible", "verifyenabled", "pierceshadow", "deriveselector", "fingerprintsection", "submitform", "retryform", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcode", "consentpassword", "scrapetable", "paginateextract", "shotelement", "captureframe", "shotcanvas"]);
|
|
@@ -1726,6 +2600,9 @@ var filesactions = /* @__PURE__ */ new Set(["batchdownload", "pausedownload", "r
|
|
|
1726
2600
|
var captureactions = /* @__PURE__ */ new Set(["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"]);
|
|
1727
2601
|
var mediaactions = /* @__PURE__ */ new Set(["capturepdf", "recordscreen", "captureaudio", "captureframe", "downloadimages", "shotcanvas", "probestream", "readmedia", "readassets", "timelapse", "convertimage", "makethumbs"]);
|
|
1728
2602
|
var httpactions = /* @__PURE__ */ new Set(["fetchurl", "parsejson", "parsehtml", "callrest", "callgraphql"]);
|
|
2603
|
+
var socketactions = /* @__PURE__ */ new Set(["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"]);
|
|
2604
|
+
var netwatchactions = /* @__PURE__ */ new Set(["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"]);
|
|
2605
|
+
var controlactions = /* @__PURE__ */ new Set(["blockrequest", "mockresponse", "rewriteheaders", "setcookies", "readcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"]);
|
|
1729
2606
|
var credentialheaders = /* @__PURE__ */ new Set(["authorization", "proxy-authorization", "cookie", "cookie2", "set-cookie", "api-key", "x-api-key", "x-auth-token", "x-session-token", "proxy-authorization"]);
|
|
1730
2607
|
var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
|
|
1731
2608
|
var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
|
|
@@ -2434,6 +3311,62 @@ function ismediakind(kind) {
|
|
|
2434
3311
|
function ishttpkind(kind) {
|
|
2435
3312
|
return httpactions.has(kind);
|
|
2436
3313
|
}
|
|
3314
|
+
function issocketkind(kind) {
|
|
3315
|
+
return socketactions.has(kind);
|
|
3316
|
+
}
|
|
3317
|
+
function isnetwatchkind(kind) {
|
|
3318
|
+
return netwatchactions.has(kind);
|
|
3319
|
+
}
|
|
3320
|
+
function iscontrolkind(kind) {
|
|
3321
|
+
return controlactions.has(kind);
|
|
3322
|
+
}
|
|
3323
|
+
function resolvedrisk(step) {
|
|
3324
|
+
if (step.kind === "capturebodies") {
|
|
3325
|
+
let options = {};
|
|
3326
|
+
try {
|
|
3327
|
+
options = parseoptions(step);
|
|
3328
|
+
} catch {
|
|
3329
|
+
options = {};
|
|
3330
|
+
}
|
|
3331
|
+
const body = options.body;
|
|
3332
|
+
const mimes = body && typeof body === "object" && !Array.isArray(body) ? body.mimes : void 0;
|
|
3333
|
+
if (Array.isArray(mimes) && mimes.some((mime) => typeof mime === "string" && privatemime(mime))) return "sensitive";
|
|
3334
|
+
return "interaction";
|
|
3335
|
+
}
|
|
3336
|
+
if (step.kind === "extractapi") {
|
|
3337
|
+
let options = {};
|
|
3338
|
+
try {
|
|
3339
|
+
options = parseoptions(step);
|
|
3340
|
+
} catch {
|
|
3341
|
+
options = {};
|
|
3342
|
+
}
|
|
3343
|
+
const replay = options.replay;
|
|
3344
|
+
const verb = replay && typeof replay === "object" && !Array.isArray(replay) ? replay.verb : void 0;
|
|
3345
|
+
if (typeof verb === "string" && !["GET", "HEAD", "OPTIONS"].includes(verb.trim().toUpperCase())) return "sensitive";
|
|
3346
|
+
return "read";
|
|
3347
|
+
}
|
|
3348
|
+
return actionrisk(step.kind);
|
|
3349
|
+
}
|
|
3350
|
+
function socketgate(session, url) {
|
|
3351
|
+
let parsed;
|
|
3352
|
+
try {
|
|
3353
|
+
parsed = new URL(url);
|
|
3354
|
+
} catch {
|
|
3355
|
+
return { allowed: false, reason: "The channel needs a valid url before it can be reviewed." };
|
|
3356
|
+
}
|
|
3357
|
+
if (parsed.protocol !== "wss:" && parsed.protocol !== "https:") return { allowed: false, reason: "Channels use wss websocket urls or https event stream urls only." };
|
|
3358
|
+
if (parsed.username || parsed.password) return { allowed: false, reason: "Channel credentials are not allowed in the url." };
|
|
3359
|
+
const origin = channelorigin(url);
|
|
3360
|
+
if (!origingranted(session, origin)) return { allowed: false, reason: `The channel to ${origin} stays outside the session origin grants.` };
|
|
3361
|
+
return { allowed: true };
|
|
3362
|
+
}
|
|
3363
|
+
function watchgate(session, settings, now) {
|
|
3364
|
+
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the request watch." };
|
|
3365
|
+
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot watch requests." };
|
|
3366
|
+
if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot watch requests." };
|
|
3367
|
+
if (settings?.webrequestgrant !== true) return { allowed: false, reason: "Request watching needs the webrequest grant in the review panel first; the observation derives from the page timing buffers and adds no manifest permission." };
|
|
3368
|
+
return { allowed: true };
|
|
3369
|
+
}
|
|
2437
3370
|
function origincheck(session, url) {
|
|
2438
3371
|
let parsed;
|
|
2439
3372
|
try {
|
|
@@ -2599,6 +3532,265 @@ function fetchnumeric(options, key) {
|
|
|
2599
3532
|
function isrecordingkind(kind) {
|
|
2600
3533
|
return kind === "recordscreen" || kind === "captureaudio";
|
|
2601
3534
|
}
|
|
3535
|
+
function validatesocketgrammar(step, options) {
|
|
3536
|
+
const kind = step.kind;
|
|
3537
|
+
if (kind === "opensocket") {
|
|
3538
|
+
const channel = channeloptionsof(options.socket);
|
|
3539
|
+
if (!channel) return { allowed: false, reason: "A reviewed socket with a url is required in options.socket." };
|
|
3540
|
+
if (channel.options.reconnect !== void 0 && !Number.isInteger(channel.options.reconnect)) return { allowed: false, reason: "The reviewed socket reconnect budget must be an integer attempt count with no code ceiling." };
|
|
3541
|
+
for (const label of ["backoff", "backoffceiling"]) {
|
|
3542
|
+
const value = channel.options[label];
|
|
3543
|
+
if (value !== void 0 && (typeof value !== "number" || !Number.isFinite(value) || value < 0)) return { allowed: false, reason: `The reviewed socket ${label} must be zero or a positive number of milliseconds with no code ceiling.` };
|
|
3544
|
+
}
|
|
3545
|
+
if (channel.options.lifetime !== void 0 && (typeof channel.options.lifetime !== "number" || !Number.isFinite(channel.options.lifetime) || channel.options.lifetime <= 0)) return { allowed: false, reason: "The reviewed socket lifetime window must be a positive number of milliseconds." };
|
|
3546
|
+
}
|
|
3547
|
+
if (kind === "sendmessage") {
|
|
3548
|
+
const message = options.message;
|
|
3549
|
+
if (!message || typeof message !== "object" || Array.isArray(message)) return { allowed: false, reason: "A reviewed message with a channel, stream and payload is required in options.message." };
|
|
3550
|
+
const envelope = message;
|
|
3551
|
+
if (!isnonempty(envelope.channel)) return { allowed: false, reason: "The reviewed message needs the open channel id in options.message.channel." };
|
|
3552
|
+
if (envelope.stream !== void 0 && !isnonempty(envelope.stream)) return { allowed: false, reason: "The reviewed message stream name must be a non-empty string." };
|
|
3553
|
+
if (typeof envelope.payload !== "string") return { allowed: false, reason: "The reviewed message payload must be a string." };
|
|
3554
|
+
}
|
|
3555
|
+
if (kind === "waitmessage") {
|
|
3556
|
+
if (options.filter !== void 0) {
|
|
3557
|
+
const filter = options.filter;
|
|
3558
|
+
if (!filter || typeof filter !== "object" || Array.isArray(filter)) return { allowed: false, reason: "The reviewed message filter must be an object of stream, path and limit." };
|
|
3559
|
+
const reviewed = filter;
|
|
3560
|
+
if (reviewed.stream !== void 0 && !isnonempty(reviewed.stream)) return { allowed: false, reason: "The reviewed message filter stream name must be a non-empty string." };
|
|
3561
|
+
if (reviewed.path !== void 0 && (typeof reviewed.path !== "string" || !validpath(reviewed.path.trim()))) return { allowed: false, reason: "The reviewed message filter path must be a dotted path of non-empty segments." };
|
|
3562
|
+
if (reviewed.limit !== void 0 && (typeof reviewed.limit !== "number" || !Number.isInteger(reviewed.limit) || reviewed.limit < 1)) return { allowed: false, reason: "The reviewed message match limit must be a positive integer with no code ceiling." };
|
|
3563
|
+
}
|
|
3564
|
+
if (options.wait !== void 0 && (typeof options.wait !== "number" || !Number.isFinite(options.wait) || options.wait < 0)) return { allowed: false, reason: "The reviewed message wait budget must be zero or a positive number of milliseconds." };
|
|
3565
|
+
}
|
|
3566
|
+
if (kind === "subscribesse") {
|
|
3567
|
+
const subscription = subscriptionoptionsof(options.subscription);
|
|
3568
|
+
if (!subscription) return { allowed: false, reason: "A reviewed subscription with an event stream url and a cancellation path is required in options.subscription." };
|
|
3569
|
+
const rawlifetime = options.subscription && typeof options.subscription === "object" && !Array.isArray(options.subscription) ? options.subscription.lifetime : void 0;
|
|
3570
|
+
if (rawlifetime !== void 0 && (typeof rawlifetime !== "number" || !Number.isFinite(rawlifetime) || rawlifetime <= 0)) return { allowed: false, reason: "The reviewed subscription lifetime window must be a positive number of milliseconds." };
|
|
3571
|
+
}
|
|
3572
|
+
if (kind === "longpoll") {
|
|
3573
|
+
const cursor = pollcursorof(options.poll);
|
|
3574
|
+
if (!cursor) return { allowed: false, reason: "A reviewed poll cursor with a url, cursor field, interval and stop condition is required in options.poll." };
|
|
3575
|
+
const wait = options.wait;
|
|
3576
|
+
if (wait !== void 0 && (typeof wait !== "number" || !Number.isFinite(wait) || wait < 0)) return { allowed: false, reason: "The reviewed long poll wait budget must be zero or a positive number of milliseconds." };
|
|
3577
|
+
if (wait !== void 0 && cursor.interval > wait) return { allowed: false, reason: `The long poll interval of ${cursor.interval} milliseconds exceeds the reviewed wait budget of ${wait} milliseconds; review a wider budget or a shorter interval.` };
|
|
3578
|
+
}
|
|
3579
|
+
return { allowed: true };
|
|
3580
|
+
}
|
|
3581
|
+
function validatenetwatchgrammar(step, options) {
|
|
3582
|
+
const kind = step.kind;
|
|
3583
|
+
if (kind === "watchrequests") {
|
|
3584
|
+
if (options.watch !== void 0) {
|
|
3585
|
+
const watch = options.watch;
|
|
3586
|
+
if (!watch || typeof watch !== "object" || Array.isArray(watch)) return { allowed: false, reason: "The reviewed watch window must be an object." };
|
|
3587
|
+
const reviewed = watch;
|
|
3588
|
+
if (reviewed.window !== void 0 && (typeof reviewed.window !== "number" || !Number.isFinite(reviewed.window) || reviewed.window < 0)) return { allowed: false, reason: "The reviewed watch window must be zero or a positive number of milliseconds." };
|
|
3589
|
+
}
|
|
3590
|
+
if (options.limit !== void 0 && (typeof options.limit !== "number" || !Number.isInteger(options.limit) || options.limit < 1)) return { allowed: false, reason: "The reviewed watch match limit must be a positive integer with no code ceiling." };
|
|
3591
|
+
}
|
|
3592
|
+
if (kind === "readheaders") {
|
|
3593
|
+
const headers = options.headers;
|
|
3594
|
+
if (!headers || typeof headers !== "object" || Array.isArray(headers)) return { allowed: false, reason: "A reviewed header filter with a name allowlist and a redaction list is required in options.headers." };
|
|
3595
|
+
const reviewed = headers;
|
|
3596
|
+
if (!Array.isArray(reviewed.allow) || reviewed.allow.length === 0 || !reviewed.allow.every((name) => isnonempty(name))) return { allowed: false, reason: "The reviewed header allowlist must be a non-empty list of header names." };
|
|
3597
|
+
if (!Array.isArray(reviewed.redact) || reviewed.redact.length === 0 || !reviewed.redact.every((name) => isnonempty(name))) return { allowed: false, reason: "Header capture requires a reviewed redaction list before any header value is stored." };
|
|
3598
|
+
}
|
|
3599
|
+
if (kind === "capturebodies") {
|
|
3600
|
+
const body = options.body;
|
|
3601
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return { allowed: false, reason: "A reviewed body filter with a url pattern, mime list and byte ceiling is required in options.body." };
|
|
3602
|
+
const reviewed = body;
|
|
3603
|
+
if (reviewed.urlpattern !== void 0 && !isnonempty(reviewed.urlpattern)) return { allowed: false, reason: "The reviewed body url pattern must be a non-empty string." };
|
|
3604
|
+
if (reviewed.mimes !== void 0 && (!Array.isArray(reviewed.mimes) || reviewed.mimes.length === 0 || !reviewed.mimes.every((mime) => isnonempty(mime)))) return { allowed: false, reason: "The reviewed body mime list must be a non-empty list of mime types." };
|
|
3605
|
+
if (reviewed.ceiling !== void 0 && (typeof reviewed.ceiling !== "number" || !Number.isFinite(reviewed.ceiling) || reviewed.ceiling < 0)) return { allowed: false, reason: "The reviewed body byte ceiling must be zero or a positive number of bytes with no code ceiling." };
|
|
3606
|
+
}
|
|
3607
|
+
if (kind === "mapapi") {
|
|
3608
|
+
if (options.limit !== void 0 && (typeof options.limit !== "number" || !Number.isInteger(options.limit) || options.limit < 1)) return { allowed: false, reason: "The reviewed mapapi match limit must be a positive integer with no code ceiling." };
|
|
3609
|
+
}
|
|
3610
|
+
if (kind === "extractapi") {
|
|
3611
|
+
const replay = apireplayspecof(options.replay);
|
|
3612
|
+
if (!replay) return { allowed: false, reason: "A reviewed replay spec with an endpoint is required in options.replay." };
|
|
3613
|
+
if (!ishttpsurl(replay.endpoint)) return { allowed: false, reason: "The reviewed replay endpoint must be an HTTPS url." };
|
|
3614
|
+
if (replay.verb !== void 0 && !["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"].includes(replay.verb)) return { allowed: false, reason: "The reviewed replay verb must be a known HTTP verb." };
|
|
3615
|
+
for (const path of replay.paths ?? []) {
|
|
3616
|
+
if (!validpath(path.trim())) return { allowed: false, reason: `The reviewed replay extraction path ${path} must be a dotted path of non-empty segments.` };
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
return { allowed: true };
|
|
3620
|
+
}
|
|
3621
|
+
function validatecontrolgrammar(step, options) {
|
|
3622
|
+
const kind = step.kind;
|
|
3623
|
+
if (kind === "blockrequest") {
|
|
3624
|
+
const rule = blockruleof(options.block);
|
|
3625
|
+
if (!rule) return { allowed: false, reason: "A reviewed block rule with a url pattern is required in options.block." };
|
|
3626
|
+
if (patternorigin(rule.urlpattern) === void 0) return { allowed: false, reason: "Block rules need an https origin pattern; patterns without a named origin are refused." };
|
|
3627
|
+
if (options.block.reviewed !== true) return { allowed: false, reason: "The block rule carries the explicit reviewed flag before any request is blocked." };
|
|
3628
|
+
}
|
|
3629
|
+
if (kind === "mockresponse") {
|
|
3630
|
+
const spec = mockspecof(options.mock);
|
|
3631
|
+
if (!spec) return { allowed: false, reason: "A reviewed mock fixture with a url pattern, status and its reviewed body or a captured body ref is required in options.mock." };
|
|
3632
|
+
if (patternorigin(spec.urlpattern) === void 0) return { allowed: false, reason: "Mock fixtures need an https origin pattern; patterns without a named origin are refused." };
|
|
3633
|
+
if (spec.reviewed !== true) return { allowed: false, reason: "Every mock fixture is reviewed with its full body or the referenced captured body through the explicit reviewed flag before it serves." };
|
|
3634
|
+
}
|
|
3635
|
+
if (kind === "rewriteheaders") {
|
|
3636
|
+
const rules = options.rules;
|
|
3637
|
+
if (!Array.isArray(rules) || rules.length === 0) return { allowed: false, reason: "A reviewed non-empty list of header rewrite rules is required in options.rules." };
|
|
3638
|
+
for (const item of rules) {
|
|
3639
|
+
const rule = headeruleof(item);
|
|
3640
|
+
if (!rule) return { allowed: false, reason: "Every header rewrite rule needs a url pattern, header name, a set, append or remove operation and its value." };
|
|
3641
|
+
if (patternorigin(rule.urlpattern) === void 0) return { allowed: false, reason: "Header rewrite rules must name their origin pattern explicitly; patterns without a named origin are refused." };
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
if (kind === "setcookies") {
|
|
3645
|
+
const cookies = options.cookies;
|
|
3646
|
+
if (!Array.isArray(cookies) || cookies.length === 0) return { allowed: false, reason: "A reviewed non-empty list of cookie records is required in options.cookies." };
|
|
3647
|
+
for (const item of cookies) {
|
|
3648
|
+
if (!cookierecordof(item)) return { allowed: false, reason: "Every cookie record needs a name, domain, path and reviewed string value with an optional expiry." };
|
|
3649
|
+
}
|
|
3650
|
+
}
|
|
3651
|
+
if (kind === "readcookies" && options.domain !== void 0 && !isnonempty(options.domain)) return { allowed: false, reason: "The reviewed cookie read domain must be a non-empty host." };
|
|
3652
|
+
if (kind === "clearcookies") {
|
|
3653
|
+
if (!isnonempty(options.domain)) return { allowed: false, reason: "A reviewed cookie domain is required before cookies are cleared." };
|
|
3654
|
+
if (options.names !== void 0 && (!Array.isArray(options.names) || options.names.length === 0 || !options.names.every((name) => isnonempty(name)))) return { allowed: false, reason: "The reviewed cookie clear list must be a non-empty list of cookie names when present." };
|
|
3655
|
+
}
|
|
3656
|
+
if (kind === "authflow") {
|
|
3657
|
+
const flow = oauthflowof(options.oauth);
|
|
3658
|
+
if (!flow) return { allowed: false, reason: "A reviewed oauth flow with provider, authorize url, token url, scopes and redirect origin is required in options.oauth." };
|
|
3659
|
+
if (!ishttpsurl(flow.authorizeurl) || !ishttpsurl(flow.tokenurl)) return { allowed: false, reason: "The oauth authorize and token urls must use HTTPS." };
|
|
3660
|
+
if (!ishttpsurl(flow.redirectorigin) && !/^https:\/\/[^/]+\/?$/.test(flow.redirectorigin)) return { allowed: false, reason: "The oauth redirect origin must be an HTTPS origin inside the grants." };
|
|
3661
|
+
const consent = authconsentgranted(step);
|
|
3662
|
+
if (!consent.allowed) return consent;
|
|
3663
|
+
}
|
|
3664
|
+
if (kind === "saveapikey") {
|
|
3665
|
+
const key = options.key;
|
|
3666
|
+
if (!key || typeof key !== "object" || Array.isArray(key)) return { allowed: false, reason: "A reviewed api key entry with name, origin scopes and header is required in options.key." };
|
|
3667
|
+
const entry = key;
|
|
3668
|
+
if (!isnonempty(entry.name)) return { allowed: false, reason: "The api key entry needs a reviewed non-empty name." };
|
|
3669
|
+
if (!Array.isArray(entry.origins) || entry.origins.length === 0 || !entry.origins.every((item) => ishttpsurl(item))) return { allowed: false, reason: "The api key needs a reviewed non-empty list of HTTPS origin scopes." };
|
|
3670
|
+
if (!isnonempty(entry.header)) return { allowed: false, reason: "The api key entry needs a reviewed non-empty header name." };
|
|
3671
|
+
if (typeof entry.value !== "string" || !entry.value) return { allowed: false, reason: "The api key needs its secret value in the reviewed options; it never enters the audit trail." };
|
|
3672
|
+
const consent = apikeyconsentgranted(step);
|
|
3673
|
+
if (!consent.allowed) return consent;
|
|
3674
|
+
}
|
|
3675
|
+
if (kind === "routeproxy") {
|
|
3676
|
+
if (!proxyrouteof(options.proxy)) return { allowed: false, reason: "A reviewed proxy route with scheme, host, port and a non-empty bypass list is required in options.proxy." };
|
|
3677
|
+
if (!isnonempty(options.consentref)) return { allowed: false, reason: "Proxy routing needs the explicit reviewed consent ref before any route applies." };
|
|
3678
|
+
}
|
|
3679
|
+
if (kind === "postform") {
|
|
3680
|
+
const form = formpayloadof(options.form);
|
|
3681
|
+
if (!form) return { allowed: false, reason: "A reviewed form payload with a url and a non-empty field list is required in options.form." };
|
|
3682
|
+
if (!ishttpsurl(form.url)) return { allowed: false, reason: "The form submission target must use HTTPS." };
|
|
3683
|
+
if (options.wait !== void 0 && (typeof options.wait !== "number" || !Number.isFinite(options.wait) || options.wait < 0)) return { allowed: false, reason: "The reviewed rate limit wait budget must be zero or a positive number of milliseconds." };
|
|
3684
|
+
}
|
|
3685
|
+
if (kind === "postfiles") {
|
|
3686
|
+
const upload = multipartpayloadof(options.upload);
|
|
3687
|
+
if (!upload) return { allowed: false, reason: "A reviewed multipart upload with a url and reviewed files is required in options.upload; every file carries the explicit reviewed flag." };
|
|
3688
|
+
if (!ishttpsurl(upload.url)) return { allowed: false, reason: "The multipart upload target must use HTTPS." };
|
|
3689
|
+
if (options.wait !== void 0 && (typeof options.wait !== "number" || !Number.isFinite(options.wait) || options.wait < 0)) return { allowed: false, reason: "The reviewed rate limit wait budget must be zero or a positive number of milliseconds." };
|
|
3690
|
+
}
|
|
3691
|
+
return { allowed: true };
|
|
3692
|
+
}
|
|
3693
|
+
function blockgate(session, step, now) {
|
|
3694
|
+
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the request block." };
|
|
3695
|
+
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot block requests." };
|
|
3696
|
+
if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot block requests." };
|
|
3697
|
+
let options = {};
|
|
3698
|
+
try {
|
|
3699
|
+
options = parseoptions(step);
|
|
3700
|
+
} catch {
|
|
3701
|
+
options = {};
|
|
3702
|
+
}
|
|
3703
|
+
const rule = options.block;
|
|
3704
|
+
if (!rule || typeof rule !== "object" || Array.isArray(rule) || rule.reviewed !== true) return { allowed: false, reason: "Request blocking needs its reviewed block rule with the explicit reviewed flag before any rule applies." };
|
|
3705
|
+
if (!blockruleof(rule)) return { allowed: false, reason: "The block rule needs a url pattern and an optional resource type list." };
|
|
3706
|
+
return { allowed: true };
|
|
3707
|
+
}
|
|
3708
|
+
function cookiegate(session, domain, now) {
|
|
3709
|
+
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the cookie operation." };
|
|
3710
|
+
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot touch cookies." };
|
|
3711
|
+
if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot touch cookies." };
|
|
3712
|
+
const grants = session.grants ?? [session.origin];
|
|
3713
|
+
if (!cookiedomaingranted(domain, grants)) return { allowed: false, reason: `The cookie domain ${domain} stays outside the session origin grants; cookie control refuses domains beyond the grants.` };
|
|
3714
|
+
return { allowed: true };
|
|
3715
|
+
}
|
|
3716
|
+
function proxygate(session, step, now) {
|
|
3717
|
+
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the proxy route." };
|
|
3718
|
+
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot change routing." };
|
|
3719
|
+
if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot change routing." };
|
|
3720
|
+
let options = {};
|
|
3721
|
+
try {
|
|
3722
|
+
options = parseoptions(step);
|
|
3723
|
+
} catch {
|
|
3724
|
+
options = {};
|
|
3725
|
+
}
|
|
3726
|
+
if (!isnonempty(options.consentref)) return { allowed: false, reason: "Proxy routing needs the explicit reviewed consent ref before any route applies." };
|
|
3727
|
+
if (!proxyrouteof(options.proxy)) return { allowed: false, reason: "The proxy route needs a scheme, host, port and a non-empty bypass list of origins that stay direct." };
|
|
3728
|
+
return { allowed: true };
|
|
3729
|
+
}
|
|
3730
|
+
function authconsentgranted(step) {
|
|
3731
|
+
let options = {};
|
|
3732
|
+
try {
|
|
3733
|
+
options = parseoptions(step);
|
|
3734
|
+
} catch {
|
|
3735
|
+
options = {};
|
|
3736
|
+
}
|
|
3737
|
+
const consentref = options.consentref;
|
|
3738
|
+
if (typeof consentref !== "string" || !consentref.trim()) return { allowed: false, reason: "An oauth flow requires the reviewed provider consent prompt ref in options before it starts." };
|
|
3739
|
+
return { allowed: true };
|
|
3740
|
+
}
|
|
3741
|
+
function apikeyconsentgranted(step) {
|
|
3742
|
+
let options = {};
|
|
3743
|
+
try {
|
|
3744
|
+
options = parseoptions(step);
|
|
3745
|
+
} catch {
|
|
3746
|
+
options = {};
|
|
3747
|
+
}
|
|
3748
|
+
const consentref = options.consentref;
|
|
3749
|
+
if (typeof consentref !== "string" || !consentref.trim()) return { allowed: false, reason: "Storing an api key requires the explicit reviewed consent prompt ref in options before anything is stored." };
|
|
3750
|
+
return { allowed: true };
|
|
3751
|
+
}
|
|
3752
|
+
function ratelimitbudgetallowed(wait, budget) {
|
|
3753
|
+
if (wait !== void 0 && (typeof wait !== "number" || !Number.isFinite(wait) || wait < 0)) return { allowed: false, reason: "The rate limit wait must be zero or a positive number of milliseconds." };
|
|
3754
|
+
if (budget !== void 0 && (typeof budget !== "number" || !Number.isFinite(budget) || budget < 0)) return { allowed: false, reason: "The reviewed rate limit budget must be zero or a positive number of milliseconds." };
|
|
3755
|
+
if (wait !== void 0 && budget !== void 0 && wait > budget) return { allowed: false, reason: `The rate limit wait of ${wait} milliseconds exceeds the reviewed budget of ${budget} milliseconds; review a wider budget or submit later.` };
|
|
3756
|
+
return { allowed: true };
|
|
3757
|
+
}
|
|
3758
|
+
function controltarget(step) {
|
|
3759
|
+
let options = {};
|
|
3760
|
+
try {
|
|
3761
|
+
options = parseoptions(step);
|
|
3762
|
+
} catch {
|
|
3763
|
+
options = {};
|
|
3764
|
+
}
|
|
3765
|
+
for (const key of ["form", "upload"]) {
|
|
3766
|
+
const value = options[key];
|
|
3767
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
3768
|
+
const url = value.url;
|
|
3769
|
+
if (typeof url === "string" && url.trim()) return url.trim();
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3772
|
+
if (step.kind === "authflow") {
|
|
3773
|
+
const flow = oauthflowof(options.oauth);
|
|
3774
|
+
if (flow) return flow.tokenurl;
|
|
3775
|
+
}
|
|
3776
|
+
return void 0;
|
|
3777
|
+
}
|
|
3778
|
+
function sockettarget(step) {
|
|
3779
|
+
let options = {};
|
|
3780
|
+
try {
|
|
3781
|
+
options = parseoptions(step);
|
|
3782
|
+
} catch {
|
|
3783
|
+
options = {};
|
|
3784
|
+
}
|
|
3785
|
+
for (const key of ["socket", "subscription", "poll"]) {
|
|
3786
|
+
const value = options[key];
|
|
3787
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
3788
|
+
const url = value.url;
|
|
3789
|
+
if (typeof url === "string" && url.trim()) return url.trim();
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
return void 0;
|
|
3793
|
+
}
|
|
2602
3794
|
function mediagate(session, tabid, origin, now) {
|
|
2603
3795
|
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the media capture." };
|
|
2604
3796
|
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot capture media." };
|
|
@@ -2956,6 +4148,18 @@ function validatestep(step, origin) {
|
|
|
2956
4148
|
const httpcheck = validatehttpgrammar(step, options);
|
|
2957
4149
|
if (!httpcheck.allowed) return httpcheck;
|
|
2958
4150
|
}
|
|
4151
|
+
if (issocketkind(step.kind)) {
|
|
4152
|
+
const socketcheck = validatesocketgrammar(step, options);
|
|
4153
|
+
if (!socketcheck.allowed) return socketcheck;
|
|
4154
|
+
}
|
|
4155
|
+
if (isnetwatchkind(step.kind)) {
|
|
4156
|
+
const netwatchcheck = validatenetwatchgrammar(step, options);
|
|
4157
|
+
if (!netwatchcheck.allowed) return netwatchcheck;
|
|
4158
|
+
}
|
|
4159
|
+
if (iscontrolkind(step.kind)) {
|
|
4160
|
+
const controlcheck = validatecontrolgrammar(step, options);
|
|
4161
|
+
if (!controlcheck.allowed) return controlcheck;
|
|
4162
|
+
}
|
|
2959
4163
|
if (step.kind === "tabcreate") {
|
|
2960
4164
|
if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
|
|
2961
4165
|
if (options.window !== void 0 && (typeof options.window !== "number" || !Number.isInteger(options.window) || options.window < 0)) return { allowed: false, reason: "The reviewed target window id must be a non-negative integer." };
|
|
@@ -3046,6 +4250,79 @@ function canexecute(input) {
|
|
|
3046
4250
|
if (!consentgate.allowed) return consentgate;
|
|
3047
4251
|
}
|
|
3048
4252
|
}
|
|
4253
|
+
if (issocketkind(input.step.kind)) {
|
|
4254
|
+
const channelurl = sockettarget(input.step);
|
|
4255
|
+
if (channelurl !== void 0) {
|
|
4256
|
+
const channelgate = socketgate(input.session, channelurl);
|
|
4257
|
+
if (!channelgate.allowed) return channelgate;
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
if (input.step.kind === "watchrequests") {
|
|
4261
|
+
const watchgatecheck = watchgate(input.session, input.settings, now);
|
|
4262
|
+
if (!watchgatecheck.allowed) return watchgatecheck;
|
|
4263
|
+
}
|
|
4264
|
+
if (iscontrolkind(input.step.kind)) {
|
|
4265
|
+
const controlgate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "control the network" });
|
|
4266
|
+
if (!controlgate.allowed) return controlgate;
|
|
4267
|
+
let controloptions = {};
|
|
4268
|
+
try {
|
|
4269
|
+
controloptions = parseoptions(input.step);
|
|
4270
|
+
} catch {
|
|
4271
|
+
controloptions = {};
|
|
4272
|
+
}
|
|
4273
|
+
if (input.step.kind === "blockrequest") {
|
|
4274
|
+
const blockgatecheck = blockgate(input.session, input.step, now);
|
|
4275
|
+
if (!blockgatecheck.allowed) return blockgatecheck;
|
|
4276
|
+
const rule = blockruleof(controloptions.block);
|
|
4277
|
+
if (rule) {
|
|
4278
|
+
const blockorigin = origincheck(input.session, rule.urlpattern);
|
|
4279
|
+
if (!blockorigin.allowed) return blockorigin;
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
4282
|
+
if (input.step.kind === "mockresponse" || input.step.kind === "rewriteheaders") {
|
|
4283
|
+
const patterns = input.step.kind === "mockresponse" ? [mockspecof(controloptions.mock)?.urlpattern ?? ""] : Array.isArray(controloptions.rules) ? controloptions.rules.map((item) => item && typeof item === "object" && !Array.isArray(item) ? String(item.urlpattern ?? "") : "") : [];
|
|
4284
|
+
for (const pattern of patterns) {
|
|
4285
|
+
const patterngate = origincheck(input.session, pattern);
|
|
4286
|
+
if (!patterngate.allowed) return patterngate;
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
if (input.step.kind === "setcookies" || input.step.kind === "readcookies" || input.step.kind === "clearcookies") {
|
|
4290
|
+
const domain = typeof controloptions.domain === "string" && controloptions.domain.trim() ? controloptions.domain : Array.isArray(controloptions.cookies) ? String(controloptions.cookies[0]?.domain ?? "") : "";
|
|
4291
|
+
if (!domain) return { allowed: false, reason: "A reviewed cookie domain is required before cookie control runs." };
|
|
4292
|
+
const cookiegatecheck = cookiegate(input.session, domain, now);
|
|
4293
|
+
if (!cookiegatecheck.allowed) return cookiegatecheck;
|
|
4294
|
+
}
|
|
4295
|
+
if (input.step.kind === "authflow") {
|
|
4296
|
+
const authconsent = authconsentgranted(input.step);
|
|
4297
|
+
if (!authconsent.allowed) return authconsent;
|
|
4298
|
+
}
|
|
4299
|
+
if (input.step.kind === "saveapikey") {
|
|
4300
|
+
const keyconsent = apikeyconsentgranted(input.step);
|
|
4301
|
+
if (!keyconsent.allowed) return keyconsent;
|
|
4302
|
+
}
|
|
4303
|
+
if (input.step.kind === "routeproxy") {
|
|
4304
|
+
const proxygatecheck = proxygate(input.session, input.step, now);
|
|
4305
|
+
if (!proxygatecheck.allowed) return proxygatecheck;
|
|
4306
|
+
}
|
|
4307
|
+
const target = controltarget(input.step);
|
|
4308
|
+
if (target !== void 0) {
|
|
4309
|
+
const targetgate = origincheck(input.session, target);
|
|
4310
|
+
if (!targetgate.allowed) return targetgate;
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
if (input.step.kind === "extractapi") {
|
|
4314
|
+
let replayoptions = {};
|
|
4315
|
+
try {
|
|
4316
|
+
replayoptions = parseoptions(input.step);
|
|
4317
|
+
} catch {
|
|
4318
|
+
replayoptions = {};
|
|
4319
|
+
}
|
|
4320
|
+
const replay = apireplayspecof(replayoptions.replay);
|
|
4321
|
+
if (replay !== void 0) {
|
|
4322
|
+
const replaygate = origincheck(input.session, replay.endpoint);
|
|
4323
|
+
if (!replaygate.allowed) return replaygate;
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
3049
4326
|
if (input.step.kind === "openlink" || input.step.kind === "openprivate" || input.step.kind === "batchopen" || input.step.kind === "prefetch" || input.step.kind === "deeplink" || input.step.kind === "reopentab") {
|
|
3050
4327
|
let options = {};
|
|
3051
4328
|
try {
|
|
@@ -3067,7 +4344,7 @@ function canexecute(input) {
|
|
|
3067
4344
|
}
|
|
3068
4345
|
|
|
3069
4346
|
// version.ts
|
|
3070
|
-
var packageversion = "1.1.
|
|
4347
|
+
var packageversion = "1.1.44";
|
|
3071
4348
|
|
|
3072
4349
|
// types.ts
|
|
3073
4350
|
var protocolversion = packageversion;
|
|
@@ -3088,6 +4365,10 @@ function parseproposal(value, origin, grants) {
|
|
|
3088
4365
|
const planinput = record(root.plan);
|
|
3089
4366
|
const stepsinput = planinput.steps;
|
|
3090
4367
|
if (!Array.isArray(stepsinput) || stepsinput.length === 0) throw new Error("A plan needs at least one step.");
|
|
4368
|
+
const createdat = Date.now();
|
|
4369
|
+
const expiresat = typeof planinput.expiresat === "number" ? planinput.expiresat : createdat + 10 * 60 * 1e3;
|
|
4370
|
+
if (expiresat <= createdat) throw new Error("Plan expiry must be in the future.");
|
|
4371
|
+
const planwindow = expiresat - createdat;
|
|
3091
4372
|
const steps = stepsinput.map((input, index) => {
|
|
3092
4373
|
const candidate = record(input);
|
|
3093
4374
|
const kind = text(candidate.kind, `step ${index + 1} kind`);
|
|
@@ -3095,11 +4376,32 @@ function parseproposal(value, origin, grants) {
|
|
|
3095
4376
|
id: typeof candidate.id === "string" ? candidate.id : crypto.randomUUID(),
|
|
3096
4377
|
kind,
|
|
3097
4378
|
summary: text(candidate.summary, `step ${index + 1} summary`),
|
|
3098
|
-
risk:
|
|
4379
|
+
risk: resolvedrisk(stepof(kind, candidate, index)),
|
|
3099
4380
|
...typeof candidate.target === "string" ? { target: candidate.target } : {},
|
|
3100
4381
|
...typeof candidate.value === "string" ? { value: candidate.value } : {},
|
|
3101
4382
|
...typeof candidate.options === "string" ? { options: candidate.options } : {}
|
|
3102
4383
|
};
|
|
4384
|
+
if (step.kind === "blockrequest") {
|
|
4385
|
+
let blockoptions = {};
|
|
4386
|
+
try {
|
|
4387
|
+
blockoptions = parseoptions(step);
|
|
4388
|
+
} catch {
|
|
4389
|
+
blockoptions = {};
|
|
4390
|
+
}
|
|
4391
|
+
const rule = blockruleof(blockoptions.block);
|
|
4392
|
+
if (rule && patternorigin(rule.urlpattern) === void 0) throw new Error("Block rules without a named origin pattern are refused.");
|
|
4393
|
+
}
|
|
4394
|
+
if (step.kind === "routeproxy") {
|
|
4395
|
+
let proxyoptions = {};
|
|
4396
|
+
try {
|
|
4397
|
+
proxyoptions = parseoptions(step);
|
|
4398
|
+
} catch {
|
|
4399
|
+
proxyoptions = {};
|
|
4400
|
+
}
|
|
4401
|
+
const proxy = proxyoptions.proxy;
|
|
4402
|
+
const bypass = proxy && typeof proxy === "object" && !Array.isArray(proxy) ? proxy.bypass : void 0;
|
|
4403
|
+
if (!Array.isArray(bypass) || bypass.length === 0) throw new Error("Proxy routes without a bypass list are refused.");
|
|
4404
|
+
}
|
|
3103
4405
|
const evaluation = validatestep(step, origin);
|
|
3104
4406
|
if (!evaluation.allowed) throw new Error(evaluation.reason);
|
|
3105
4407
|
const target = outboundtarget(step);
|
|
@@ -3113,6 +4415,29 @@ function parseproposal(value, origin, grants) {
|
|
|
3113
4415
|
});
|
|
3114
4416
|
if (!granted) throw new Error(`The fetch request to ${target} targets an origin outside the grants.`);
|
|
3115
4417
|
}
|
|
4418
|
+
const channelurl = sockettarget(step);
|
|
4419
|
+
if (channelurl !== void 0) {
|
|
4420
|
+
const channeloriginvalue = channeloriginof(channelurl);
|
|
4421
|
+
const granted = covered.some((pattern) => {
|
|
4422
|
+
try {
|
|
4423
|
+
return new URL(channelurl).origin === new URL(pattern).origin || channeloriginvalue === new URL(pattern).origin;
|
|
4424
|
+
} catch {
|
|
4425
|
+
return false;
|
|
4426
|
+
}
|
|
4427
|
+
});
|
|
4428
|
+
if (!granted) throw new Error(`The channel to ${channelurl} targets an origin outside the grants.`);
|
|
4429
|
+
}
|
|
4430
|
+
let lifetime;
|
|
4431
|
+
try {
|
|
4432
|
+
const options = parseoptions(step);
|
|
4433
|
+
for (const key of ["socket", "subscription"]) {
|
|
4434
|
+
const value2 = options[key];
|
|
4435
|
+
if (value2 && typeof value2 === "object" && !Array.isArray(value2) && typeof value2.lifetime === "number") lifetime = value2.lifetime;
|
|
4436
|
+
}
|
|
4437
|
+
} catch {
|
|
4438
|
+
lifetime = void 0;
|
|
4439
|
+
}
|
|
4440
|
+
if (lifetime !== void 0 && lifetime > planwindow) throw new Error(`The channel lifetime of ${lifetime} milliseconds exceeds the reviewed plan window of ${planwindow} milliseconds.`);
|
|
3116
4441
|
return step;
|
|
3117
4442
|
});
|
|
3118
4443
|
for (const step of steps) {
|
|
@@ -3125,8 +4450,6 @@ function parseproposal(value, origin, grants) {
|
|
|
3125
4450
|
const review = submitreviewgranted(steps, step.id);
|
|
3126
4451
|
if (!review.allowed) throw new Error(review.reason);
|
|
3127
4452
|
}
|
|
3128
|
-
const createdat = Date.now();
|
|
3129
|
-
const expiresat = typeof planinput.expiresat === "number" ? planinput.expiresat : createdat + 10 * 60 * 1e3;
|
|
3130
4453
|
const plan = {
|
|
3131
4454
|
id: typeof planinput.id === "string" ? planinput.id : crypto.randomUUID(),
|
|
3132
4455
|
objective: text(planinput.objective, "objective"),
|
|
@@ -3136,14 +4459,24 @@ function parseproposal(value, origin, grants) {
|
|
|
3136
4459
|
expiresat,
|
|
3137
4460
|
state: "pending"
|
|
3138
4461
|
};
|
|
3139
|
-
if (plan.expiresat <= createdat) throw new Error("Plan expiry must be in the future.");
|
|
3140
4462
|
return { version: protocolversion, plan };
|
|
3141
4463
|
}
|
|
4464
|
+
function stepof(kind, candidate, index) {
|
|
4465
|
+
return { id: typeof candidate.id === "string" ? candidate.id : `candidate${index + 1}`, kind, summary: typeof candidate.summary === "string" ? candidate.summary : "", risk: "read", ...typeof candidate.options === "string" ? { options: candidate.options } : {} };
|
|
4466
|
+
}
|
|
4467
|
+
function channeloriginof(url) {
|
|
4468
|
+
try {
|
|
4469
|
+
const parsed = new URL(url);
|
|
4470
|
+
return `${parsed.protocol === "wss:" ? "https:" : parsed.protocol}//${parsed.host}`;
|
|
4471
|
+
} catch {
|
|
4472
|
+
return "";
|
|
4473
|
+
}
|
|
4474
|
+
}
|
|
3142
4475
|
function requestbody(input) {
|
|
3143
4476
|
return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
|
|
3144
4477
|
}
|
|
3145
4478
|
function outcomeresponse(input) {
|
|
3146
|
-
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {} });
|
|
4479
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {} });
|
|
3147
4480
|
}
|
|
3148
4481
|
function mapresponse(input) {
|
|
3149
4482
|
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
|
|
@@ -3234,10 +4567,41 @@ function callsreport(input) {
|
|
|
3234
4567
|
});
|
|
3235
4568
|
return { version: protocolversion, calls };
|
|
3236
4569
|
}
|
|
4570
|
+
function exchangesreport(input) {
|
|
4571
|
+
return { version: protocolversion, exchanges: input.exchanges, channels: input.channels, subscriptions: input.subscriptions, apimap: input.apimap };
|
|
4572
|
+
}
|
|
4573
|
+
function authreport(input) {
|
|
4574
|
+
const tokens = input.tokens.map((token) => {
|
|
4575
|
+
const { accessstorageid, refreshstorageid, ...metadata } = token;
|
|
4576
|
+
void accessstorageid;
|
|
4577
|
+
void refreshstorageid;
|
|
4578
|
+
return metadata;
|
|
4579
|
+
});
|
|
4580
|
+
return { version: protocolversion, tokens };
|
|
4581
|
+
}
|
|
4582
|
+
function controlreport(input) {
|
|
4583
|
+
const mocks = input.mocks.map((spec) => {
|
|
4584
|
+
const { body, ...metadata } = spec;
|
|
4585
|
+
void body;
|
|
4586
|
+
return metadata;
|
|
4587
|
+
});
|
|
4588
|
+
return { version: protocolversion, blocks: input.blocks, mocks, rewrites: input.rewrites, cookies: input.cookies, proxies: input.proxies, ratelimits: input.ratelimits };
|
|
4589
|
+
}
|
|
3237
4590
|
export {
|
|
3238
4591
|
annotationplanof,
|
|
4592
|
+
apientries,
|
|
4593
|
+
apikeyconsentgranted,
|
|
4594
|
+
apireplayspecof,
|
|
4595
|
+
applyheaderules,
|
|
3239
4596
|
assetentries,
|
|
4597
|
+
authconsentgranted,
|
|
4598
|
+
authorizeurl,
|
|
4599
|
+
authreport,
|
|
3240
4600
|
blendrows,
|
|
4601
|
+
blockgate,
|
|
4602
|
+
blockruleof,
|
|
4603
|
+
bodyfilterof,
|
|
4604
|
+
bodymatches,
|
|
3241
4605
|
buildname,
|
|
3242
4606
|
buildpdf,
|
|
3243
4607
|
buildsheet,
|
|
@@ -3246,6 +4610,9 @@ export {
|
|
|
3246
4610
|
callrest,
|
|
3247
4611
|
callsreport,
|
|
3248
4612
|
canexecute,
|
|
4613
|
+
capturebody,
|
|
4614
|
+
capturecode,
|
|
4615
|
+
capturedheaders,
|
|
3249
4616
|
captureelement,
|
|
3250
4617
|
captureformats,
|
|
3251
4618
|
capturekinds,
|
|
@@ -3256,9 +4623,21 @@ export {
|
|
|
3256
4623
|
capturestitched,
|
|
3257
4624
|
capturetargets,
|
|
3258
4625
|
capturevisible,
|
|
4626
|
+
channeloptionsof,
|
|
4627
|
+
channelorigin,
|
|
4628
|
+
closechannel,
|
|
4629
|
+
collectmessages,
|
|
4630
|
+
controlkinds,
|
|
4631
|
+
controlreport,
|
|
4632
|
+
controltarget,
|
|
3259
4633
|
convertdirectiveof,
|
|
4634
|
+
cookiedomaingranted,
|
|
4635
|
+
cookiegate,
|
|
4636
|
+
cookierecordof,
|
|
4637
|
+
correlationid,
|
|
3260
4638
|
croprect,
|
|
3261
4639
|
crossesviewport,
|
|
4640
|
+
cursorfrom,
|
|
3262
4641
|
datasetresponse,
|
|
3263
4642
|
dedupeimages,
|
|
3264
4643
|
actionrisk as deriveactionrisk,
|
|
@@ -3266,16 +4645,23 @@ export {
|
|
|
3266
4645
|
downloadreport,
|
|
3267
4646
|
errorreportresponse,
|
|
3268
4647
|
eventresponse,
|
|
4648
|
+
exchangesreport,
|
|
3269
4649
|
extractionreport,
|
|
4650
|
+
extractvalues,
|
|
4651
|
+
failureclass,
|
|
3270
4652
|
fetchoptionsof,
|
|
3271
4653
|
fetchrequestof,
|
|
4654
|
+
filterexchanges,
|
|
3272
4655
|
finishrecording,
|
|
3273
4656
|
fixedheadermatch,
|
|
4657
|
+
formpayloadof,
|
|
3274
4658
|
formreportresponse,
|
|
3275
4659
|
frameinterval,
|
|
3276
4660
|
generatedvalueallowed,
|
|
3277
4661
|
graphqlopenvelope,
|
|
3278
4662
|
graphqlrequestof,
|
|
4663
|
+
headerfilterof,
|
|
4664
|
+
headeruleof,
|
|
3279
4665
|
heldkeysreport,
|
|
3280
4666
|
hostpattern,
|
|
3281
4667
|
htmlqueriesof,
|
|
@@ -3283,67 +4669,118 @@ export {
|
|
|
3283
4669
|
imagefilterof,
|
|
3284
4670
|
imagematches,
|
|
3285
4671
|
imagenames,
|
|
4672
|
+
iscontrolkind,
|
|
3286
4673
|
isformkind,
|
|
4674
|
+
isnetwatchkind,
|
|
4675
|
+
issocketkind,
|
|
3287
4676
|
iswatchkind,
|
|
3288
4677
|
jsonpathrulesof,
|
|
3289
4678
|
lapseframes,
|
|
3290
4679
|
lapseplanof,
|
|
3291
4680
|
layoutreport,
|
|
3292
4681
|
mapresponse,
|
|
4682
|
+
matchmessage,
|
|
4683
|
+
matchurlpattern,
|
|
3293
4684
|
mediaentries,
|
|
3294
4685
|
mediakinds,
|
|
3295
4686
|
mediareport,
|
|
4687
|
+
messagefilterof,
|
|
4688
|
+
mockfor,
|
|
4689
|
+
mockspecof,
|
|
4690
|
+
multipartchunks,
|
|
4691
|
+
multipartpayloadof,
|
|
3296
4692
|
navstateresponse,
|
|
3297
4693
|
netlogreport,
|
|
4694
|
+
netwatchkinds,
|
|
4695
|
+
newblockrule,
|
|
4696
|
+
newchannel,
|
|
4697
|
+
newexchange,
|
|
4698
|
+
newheaderule,
|
|
4699
|
+
newmockspec,
|
|
3298
4700
|
newrecording,
|
|
3299
4701
|
normalizeendpoint,
|
|
4702
|
+
oauthflowof,
|
|
3300
4703
|
observationmodeof,
|
|
3301
4704
|
observationresponse,
|
|
4705
|
+
openchannel,
|
|
3302
4706
|
outcomeresponse,
|
|
4707
|
+
pairexchange,
|
|
3303
4708
|
pairstates,
|
|
3304
4709
|
parsehtmlbody,
|
|
3305
4710
|
parseproposal,
|
|
4711
|
+
parsessetext,
|
|
4712
|
+
parsetokens,
|
|
3306
4713
|
passwordconsentgranted,
|
|
4714
|
+
patternorigin,
|
|
4715
|
+
payloadshapeof,
|
|
3307
4716
|
payloadvalid,
|
|
3308
4717
|
payloadwithdefaults,
|
|
3309
4718
|
pdfoptionsof,
|
|
3310
4719
|
pdfpagesize,
|
|
3311
4720
|
pdfsegments,
|
|
3312
4721
|
pdftextlayout,
|
|
4722
|
+
pollcursorof,
|
|
4723
|
+
polldecision,
|
|
4724
|
+
pollurl,
|
|
4725
|
+
privatemime,
|
|
3313
4726
|
profilegrantgranted,
|
|
3314
4727
|
protocolversion,
|
|
3315
4728
|
provenancereport,
|
|
4729
|
+
proxygate,
|
|
4730
|
+
proxyrouteof,
|
|
4731
|
+
publishmessage,
|
|
3316
4732
|
quarantinereport,
|
|
3317
4733
|
randomid,
|
|
4734
|
+
rankapis,
|
|
4735
|
+
ratelimitbudgetallowed,
|
|
4736
|
+
ratelimitreadof,
|
|
4737
|
+
ratelimitwait,
|
|
3318
4738
|
readpath,
|
|
3319
4739
|
readstream,
|
|
4740
|
+
receivemessage,
|
|
4741
|
+
reconnectwaits,
|
|
3320
4742
|
recordingoptionsof,
|
|
4743
|
+
redactedcookies,
|
|
3321
4744
|
regionsteps,
|
|
4745
|
+
replayurl,
|
|
3322
4746
|
requestbody,
|
|
3323
4747
|
resolutionverdict,
|
|
4748
|
+
resolvedrisk,
|
|
4749
|
+
resourcefacts,
|
|
4750
|
+
retryafterof,
|
|
4751
|
+
revertrule,
|
|
4752
|
+
revocationruleof,
|
|
3324
4753
|
safetyresponse,
|
|
3325
4754
|
scaledrect,
|
|
3326
4755
|
seamweights,
|
|
3327
4756
|
selectorresponse,
|
|
3328
4757
|
sendfetch,
|
|
4758
|
+
sequenceintegrity,
|
|
3329
4759
|
sessionmemory,
|
|
3330
4760
|
signalsreport,
|
|
4761
|
+
socketgate,
|
|
4762
|
+
socketkinds,
|
|
4763
|
+
sserequestheaders,
|
|
3331
4764
|
statusclassof,
|
|
3332
4765
|
streamsummaries,
|
|
3333
4766
|
streamwindowof,
|
|
3334
4767
|
submitreviewgranted,
|
|
4768
|
+
subscriptionoptionsof,
|
|
3335
4769
|
tabreportresponse,
|
|
3336
4770
|
templateurl,
|
|
3337
4771
|
thumbdirectiveof,
|
|
3338
4772
|
thumbgeometry,
|
|
4773
|
+
tokenrequest,
|
|
3339
4774
|
trailreport,
|
|
3340
4775
|
transformgrammar,
|
|
3341
4776
|
unwrapgraphql,
|
|
4777
|
+
urlencodeform,
|
|
3342
4778
|
validatefieldmatch,
|
|
3343
4779
|
validateformrecord,
|
|
3344
4780
|
validatestep,
|
|
3345
4781
|
validatetargetref,
|
|
3346
4782
|
validatevaluegen,
|
|
4783
|
+
watchgate,
|
|
3347
4784
|
wizardreport
|
|
3348
4785
|
};
|
|
3349
4786
|
//# sourceMappingURL=index.js.map
|