@hraness/kb 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DISCLOSURE +43 -0
- package/LICENSE +21 -0
- package/README.md +545 -0
- package/dist/agent-context.js +35 -0
- package/dist/agent-guide-audit.js +18 -0
- package/dist/attachments.js +19 -0
- package/dist/authoring.js +33 -0
- package/dist/benchmark.js +19 -0
- package/dist/capture.js +49 -0
- package/dist/cli.js +3646 -0
- package/dist/clip/acquire.js +57 -0
- package/dist/clip/args.js +21 -0
- package/dist/clip/bounded-byte-buffer.js +9 -0
- package/dist/clip/browser-profiles.js +17 -0
- package/dist/clip/bundle-reader.js +11 -0
- package/dist/clip/cli.js +27 -0
- package/dist/clip/cookies.js +21 -0
- package/dist/clip/defuddle-worker.js +84 -0
- package/dist/clip/doctor.js +32 -0
- package/dist/clip/jobs.js +784 -0
- package/dist/clip/network-proxy.js +9 -0
- package/dist/clip/network.js +28 -0
- package/dist/clip/persist.js +30 -0
- package/dist/clip/refresh.js +11 -0
- package/dist/clip/terminal.js +11 -0
- package/dist/evaluation-builder.js +12238 -0
- package/dist/evaluation-kb.js +26 -0
- package/dist/evaluation.js +35 -0
- package/dist/git.js +39 -0
- package/dist/graph.js +39 -0
- package/dist/index-01jj6rbv.js +1103 -0
- package/dist/index-06c9ctr6.js +738 -0
- package/dist/index-0kavxzqj.js +2480 -0
- package/dist/index-1gwbassd.js +743 -0
- package/dist/index-1n418kb9.js +827 -0
- package/dist/index-1vrd1rmn.js +56 -0
- package/dist/index-1xxnjn0d.js +171 -0
- package/dist/index-3rm7cz6h.js +373 -0
- package/dist/index-3v2z4f0q.js +328 -0
- package/dist/index-48pz4jpc.js +392 -0
- package/dist/index-4j3tt0c3.js +234 -0
- package/dist/index-5n05se68.js +307 -0
- package/dist/index-5vwpzb5a.js +764 -0
- package/dist/index-6g2pv9d2.js +370 -0
- package/dist/index-7fzc32gf.js +712 -0
- package/dist/index-84x0vjjp.js +432 -0
- package/dist/index-adx6khj5.js +488 -0
- package/dist/index-b88v3vtm.js +805 -0
- package/dist/index-cv6fh7z5.js +462 -0
- package/dist/index-cxfrakt7.js +1067 -0
- package/dist/index-d13v9ckt.js +150 -0
- package/dist/index-dycjbhw2.js +1943 -0
- package/dist/index-dyqwejk5.js +531 -0
- package/dist/index-e5fbsywq.js +658 -0
- package/dist/index-f984hw45.js +927 -0
- package/dist/index-gh719d91.js +63 -0
- package/dist/index-hgve9rh2.js +2046 -0
- package/dist/index-hya40gb2.js +474 -0
- package/dist/index-j4zgmzjr.js +152 -0
- package/dist/index-jsmvyyvf.js +1166 -0
- package/dist/index-kh0pwvdd.js +1974 -0
- package/dist/index-mqx4nd6v.js +119 -0
- package/dist/index-mxxxytys.js +1254 -0
- package/dist/index-n5dd7r0v.js +711 -0
- package/dist/index-npg9z1a4.js +431 -0
- package/dist/index-pj501bh1.js +140 -0
- package/dist/index-qry4vhxk.js +1041 -0
- package/dist/index-s2gw5aw9.js +199 -0
- package/dist/index-vvdj6kca.js +70 -0
- package/dist/index-w2zc0vwa.js +571 -0
- package/dist/index-x3fthpsc.js +585 -0
- package/dist/index-zr53sf63.js +44 -0
- package/dist/index-zxdy5pby.js +2728 -0
- package/dist/index-zzhgcwyt.js +966 -0
- package/dist/index.js +453 -0
- package/dist/navigation.js +13 -0
- package/dist/pdf.js +85 -0
- package/dist/percolate.js +24 -0
- package/dist/portfolio.js +77 -0
- package/dist/query.js +29 -0
- package/dist/repository-memory.js +60 -0
- package/dist/sdk.js +36 -0
- package/dist/search-rules.js +34 -0
- package/dist/search.js +22 -0
- package/dist/semantic.js +39 -0
- package/dist/source-inbox.js +17 -0
- package/dist/untrusted-content.js +32 -0
- package/dist/url-intelligence.js +74 -0
- package/dist/workflow.js +23 -0
- package/dist/workflows/decision-context.js +20 -0
- package/dist/workflows/explain-change.js +8 -0
- package/dist/workflows/index.js +28 -0
- package/dist/workflows/plan-radar.js +10 -0
- package/package.json +409 -0
- package/skills/kb/AGENTS.md +19 -0
- package/skills/kb/SKILL.md +82 -0
- package/skills/kb/agents/openai.yaml +4 -0
- package/skills/kb/references/pdf-review.md +51 -0
- package/skills/kb/references/percolate.md +144 -0
- package/skills/kb/references/plan-structure.md +105 -0
- package/skills/kb/references/plan.md +115 -0
- package/skills/kb/references/query.md +231 -0
- package/skills/kb/references/refresh.md +133 -0
- package/skills/kb/references/save-pdf.md +125 -0
- package/skills/kb/references/save-url.md +178 -0
- package/skills/kb/references/url-authentication.md +75 -0
- package/skills/kb/references/url-platforms.md +25 -0
- package/src/agent-context.ts +1241 -0
- package/src/agent-guide-audit.ts +750 -0
- package/src/attachments.ts +723 -0
- package/src/authoring.ts +1725 -0
- package/src/benchmark.ts +362 -0
- package/src/capture.ts +68 -0
- package/src/cli.ts +3660 -0
- package/src/clip/acquire.ts +1273 -0
- package/src/clip/archive-today.ts +402 -0
- package/src/clip/args.ts +423 -0
- package/src/clip/assets.ts +262 -0
- package/src/clip/bounded-byte-buffer.ts +75 -0
- package/src/clip/browser-profiles.ts +372 -0
- package/src/clip/bundle-reader.ts +608 -0
- package/src/clip/capture.ts +1054 -0
- package/src/clip/cli.ts +181 -0
- package/src/clip/cookies.ts +512 -0
- package/src/clip/doctor.ts +1110 -0
- package/src/clip/extract.ts +868 -0
- package/src/clip/jobs.ts +959 -0
- package/src/clip/lib.ts +861 -0
- package/src/clip/media.ts +1474 -0
- package/src/clip/metadata-search-tool/Cargo.lock +2498 -0
- package/src/clip/metadata-search-tool/Cargo.toml +18 -0
- package/src/clip/metadata-search-tool/runner.ts +685 -0
- package/src/clip/metadata-search-tool/src/main.rs +389 -0
- package/src/clip/metadata-search.ts +687 -0
- package/src/clip/network-proxy.ts +627 -0
- package/src/clip/network.ts +950 -0
- package/src/clip/package-root.ts +54 -0
- package/src/clip/persist.ts +1472 -0
- package/src/clip/platforms.ts +1824 -0
- package/src/clip/refresh.ts +197 -0
- package/src/clip/structured.ts +432 -0
- package/src/clip/terminal.ts +197 -0
- package/src/clip/url-intelligence.ts +581 -0
- package/src/clip/url-metadata-backfill.ts +616 -0
- package/src/clip/url-metadata-cli.ts +251 -0
- package/src/clip/url-metadata.ts +1397 -0
- package/src/evaluation-analysis-v2.ts +1440 -0
- package/src/evaluation-builder.ts +2539 -0
- package/src/evaluation-corpus-authoring.ts +2052 -0
- package/src/evaluation-evidence.ts +1147 -0
- package/src/evaluation-execution-v2.ts +426 -0
- package/src/evaluation-implementation.ts +131 -0
- package/src/evaluation-kb-closure.ts +1430 -0
- package/src/evaluation-kb-runner-v2.ts +735 -0
- package/src/evaluation-kb-v2.ts +2567 -0
- package/src/evaluation-kb.ts +1018 -0
- package/src/evaluation-measurement-v2.ts +1462 -0
- package/src/evaluation-packing-v2.ts +452 -0
- package/src/evaluation-redaction.ts +28 -0
- package/src/evaluation-v2.ts +3857 -0
- package/src/evaluation.ts +1220 -0
- package/src/git.ts +1276 -0
- package/src/graph.ts +1487 -0
- package/src/index.ts +20 -0
- package/src/init.ts +123 -0
- package/src/navigation.ts +277 -0
- package/src/note-lock.ts +529 -0
- package/src/pdf/args.ts +202 -0
- package/src/pdf/capture.ts +298 -0
- package/src/pdf/cli.ts +159 -0
- package/src/pdf/extract.ts +303 -0
- package/src/pdf/layout.ts +618 -0
- package/src/pdf/markdown.ts +154 -0
- package/src/pdf/model.ts +234 -0
- package/src/pdf/ocr.ts +190 -0
- package/src/pdf/persist.ts +314 -0
- package/src/pdf/source.ts +135 -0
- package/src/pdf/tools.ts +146 -0
- package/src/pdf.ts +92 -0
- package/src/percolate.ts +877 -0
- package/src/portfolio-audit.ts +574 -0
- package/src/portfolio-identity.ts +162 -0
- package/src/portfolio-registry.ts +473 -0
- package/src/portfolio.ts +765 -0
- package/src/query.ts +646 -0
- package/src/repository-memory.ts +1150 -0
- package/src/sdk.ts +1452 -0
- package/src/search-rules.ts +712 -0
- package/src/search.ts +710 -0
- package/src/semantic-runtime.ts +1139 -0
- package/src/semantic.ts +2480 -0
- package/src/source-inbox.ts +232 -0
- package/src/untrusted-content.ts +401 -0
- package/src/url-intelligence.ts +104 -0
- package/src/vault.ts +636 -0
- package/src/workflow.ts +653 -0
- package/src/workflows/decision-context.ts +81 -0
- package/src/workflows/explain-change.ts +73 -0
- package/src/workflows/index.ts +3 -0
- package/src/workflows/plan-radar.ts +101 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/clip/cookies.ts
|
|
3
|
+
import { closeSync, constants, fstatSync, openSync, readSync } from "fs";
|
|
4
|
+
import { resolve } from "path";
|
|
5
|
+
var MAX_COOKIE_RECORDS = 4096;
|
|
6
|
+
var MAX_COOKIE_BYTES = 2 * 1024 * 1024;
|
|
7
|
+
var cookieNamePattern = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
8
|
+
var cookieValuePattern = /^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]*$/;
|
|
9
|
+
var quotedCookieValuePattern = /^"[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]*"$/;
|
|
10
|
+
var cookieDomainPattern = /^[a-z0-9.-]+$/i;
|
|
11
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
|
+
var isUnknownArray = (value) => Array.isArray(value);
|
|
13
|
+
function hasControlCharacter(value) {
|
|
14
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
15
|
+
const code = value.charCodeAt(index);
|
|
16
|
+
if (code <= 31 || code === 127)
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
function canonicalHostname(value) {
|
|
22
|
+
const trimmed = value.trim().toLowerCase().replace(/^\.+/, "").replace(/\.$/, "");
|
|
23
|
+
if (trimmed === "" || trimmed.length > 253 || trimmed.includes("..") || !cookieDomainPattern.test(trimmed)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const hostname = new URL(`http://${trimmed}/`).hostname.toLowerCase().replace(/\.$/, "");
|
|
28
|
+
return hostname === "" || hostname.length > 253 ? null : hostname;
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function domainMatches(hostname, domain, hostOnly) {
|
|
34
|
+
return hostname === domain || !hostOnly && hostname.endsWith(`.${domain}`);
|
|
35
|
+
}
|
|
36
|
+
function pathMatches(requestPath, cookiePath) {
|
|
37
|
+
if (requestPath === cookiePath)
|
|
38
|
+
return true;
|
|
39
|
+
if (!requestPath.startsWith(cookiePath))
|
|
40
|
+
return false;
|
|
41
|
+
return cookiePath.endsWith("/") || requestPath[cookiePath.length] === "/";
|
|
42
|
+
}
|
|
43
|
+
function safeCookiePath(value) {
|
|
44
|
+
const path = value === undefined ? "/" : value;
|
|
45
|
+
if (typeof path !== "string" || !path.startsWith("/") || path.length > 4096 || hasControlCharacter(path))
|
|
46
|
+
return null;
|
|
47
|
+
return path;
|
|
48
|
+
}
|
|
49
|
+
function cookieExpiry(value, nowSeconds) {
|
|
50
|
+
const raw = value.expires ?? value.expirationDate;
|
|
51
|
+
if (raw === undefined || raw === null)
|
|
52
|
+
return 0;
|
|
53
|
+
if (typeof raw !== "number" || !Number.isFinite(raw))
|
|
54
|
+
return null;
|
|
55
|
+
if (raw === 0)
|
|
56
|
+
return 0;
|
|
57
|
+
if (raw <= nowSeconds || raw > 253402300799)
|
|
58
|
+
return null;
|
|
59
|
+
return Math.trunc(raw);
|
|
60
|
+
}
|
|
61
|
+
function cookieSameSite(value) {
|
|
62
|
+
if (value.sameSite === undefined || value.sameSite === null)
|
|
63
|
+
return null;
|
|
64
|
+
if (typeof value.sameSite !== "string")
|
|
65
|
+
return;
|
|
66
|
+
switch (value.sameSite.toLowerCase()) {
|
|
67
|
+
case "strict":
|
|
68
|
+
return "Strict";
|
|
69
|
+
case "lax":
|
|
70
|
+
return "Lax";
|
|
71
|
+
case "none":
|
|
72
|
+
case "no_restriction":
|
|
73
|
+
return "None";
|
|
74
|
+
case "unspecified":
|
|
75
|
+
return null;
|
|
76
|
+
default:
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function candidateDomain(value, target) {
|
|
81
|
+
const targetHostname = target.hostname.toLowerCase().replace(/^\[|\]$/g, "").replace(/\.$/, "");
|
|
82
|
+
let rawDomain;
|
|
83
|
+
if (typeof value.domain === "string")
|
|
84
|
+
rawDomain = value.domain;
|
|
85
|
+
else if (value.domain !== undefined)
|
|
86
|
+
return null;
|
|
87
|
+
let urlHostname;
|
|
88
|
+
if (value.url !== undefined) {
|
|
89
|
+
if (typeof value.url !== "string" || value.url.length > 8192)
|
|
90
|
+
return null;
|
|
91
|
+
try {
|
|
92
|
+
const url = new URL(value.url);
|
|
93
|
+
if (url.protocol !== "http:" && url.protocol !== "https:" || url.username !== "" || url.password !== "")
|
|
94
|
+
return null;
|
|
95
|
+
urlHostname = url.hostname.toLowerCase().replace(/^\[|\]$/g, "").replace(/\.$/, "");
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (rawDomain === undefined && urlHostname === undefined) {
|
|
101
|
+
return { domain: targetHostname, hostOnly: true };
|
|
102
|
+
}
|
|
103
|
+
const hadLeadingDot = rawDomain?.trim().startsWith(".") === true;
|
|
104
|
+
const domain = canonicalHostname(rawDomain ?? urlHostname ?? "");
|
|
105
|
+
if (domain === null)
|
|
106
|
+
return null;
|
|
107
|
+
const explicitHostOnly = value.hostOnly;
|
|
108
|
+
if (explicitHostOnly !== undefined && typeof explicitHostOnly !== "boolean")
|
|
109
|
+
return null;
|
|
110
|
+
const hostOnly = typeof explicitHostOnly === "boolean" ? explicitHostOnly : !hadLeadingDot;
|
|
111
|
+
if (!domainMatches(targetHostname, domain, hostOnly))
|
|
112
|
+
return null;
|
|
113
|
+
if (urlHostname !== undefined && !domainMatches(urlHostname, domain, hostOnly))
|
|
114
|
+
return null;
|
|
115
|
+
return { domain, hostOnly };
|
|
116
|
+
}
|
|
117
|
+
function hasSafeUnpartitionedProvenance(value) {
|
|
118
|
+
for (const field of ["partitionKey", "topFrameSiteKey", "top_frame_site_key", "originAttributes"]) {
|
|
119
|
+
const provenance = value[field];
|
|
120
|
+
if (provenance === undefined || provenance === null)
|
|
121
|
+
continue;
|
|
122
|
+
if (typeof provenance !== "string" || provenance.trim() !== "")
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
for (const field of ["partitioned"]) {
|
|
126
|
+
const flag = value[field];
|
|
127
|
+
if (flag === undefined || flag === null)
|
|
128
|
+
continue;
|
|
129
|
+
if (typeof flag !== "boolean" || flag)
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
for (const field of ["isPartitionedAttributeSet", "hasCrossSiteAncestor", "has_cross_site_ancestor"]) {
|
|
133
|
+
const flag = value[field];
|
|
134
|
+
if (flag === undefined || flag === null)
|
|
135
|
+
continue;
|
|
136
|
+
if (flag !== false && flag !== 0 && flag !== "0")
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
function validatedCookie(value, target, nowSeconds) {
|
|
142
|
+
if (!isRecord(value))
|
|
143
|
+
return null;
|
|
144
|
+
if (typeof value.name !== "string" || value.name.length > 1024 || !cookieNamePattern.test(value.name))
|
|
145
|
+
return null;
|
|
146
|
+
if (typeof value.value !== "string" || value.value.length > 64 * 1024 || !cookieValuePattern.test(value.value) && !quotedCookieValuePattern.test(value.value))
|
|
147
|
+
return null;
|
|
148
|
+
if (!hasSafeUnpartitionedProvenance(value))
|
|
149
|
+
return null;
|
|
150
|
+
const domain = candidateDomain(value, target);
|
|
151
|
+
const path = safeCookiePath(value.path);
|
|
152
|
+
const expires = cookieExpiry(value, nowSeconds);
|
|
153
|
+
const sameSite = cookieSameSite(value);
|
|
154
|
+
if (domain === null || path === null || expires === null || sameSite === undefined)
|
|
155
|
+
return null;
|
|
156
|
+
if (!pathMatches(target.pathname || "/", path))
|
|
157
|
+
return null;
|
|
158
|
+
if (value.secure !== undefined && typeof value.secure !== "boolean")
|
|
159
|
+
return null;
|
|
160
|
+
if (value.httpOnly !== undefined && typeof value.httpOnly !== "boolean")
|
|
161
|
+
return null;
|
|
162
|
+
const secure = value.secure === true;
|
|
163
|
+
if (secure && target.protocol !== "https:")
|
|
164
|
+
return null;
|
|
165
|
+
if (sameSite === "None" && !secure)
|
|
166
|
+
return null;
|
|
167
|
+
return {
|
|
168
|
+
name: value.name,
|
|
169
|
+
value: value.value,
|
|
170
|
+
domain: domain.domain,
|
|
171
|
+
hostOnly: domain.hostOnly,
|
|
172
|
+
path,
|
|
173
|
+
secure,
|
|
174
|
+
httpOnly: value.httpOnly === true,
|
|
175
|
+
sameSite,
|
|
176
|
+
expires
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function cookieBytes(cookie) {
|
|
180
|
+
return Buffer.byteLength(`${cookie.domain} ${cookie.path} ${cookie.name} ${cookie.value}
|
|
181
|
+
`, "utf8");
|
|
182
|
+
}
|
|
183
|
+
function filterCookies(values, target, nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
184
|
+
const bounded = values.slice(0, MAX_COOKIE_RECORDS);
|
|
185
|
+
let rejected = Math.max(0, values.length - bounded.length);
|
|
186
|
+
let totalBytes = 0;
|
|
187
|
+
const cookies = new Map;
|
|
188
|
+
for (const value of bounded) {
|
|
189
|
+
const cookie = validatedCookie(value, target, nowSeconds);
|
|
190
|
+
if (cookie === null) {
|
|
191
|
+
rejected += 1;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const key = `${cookie.domain}\x00${cookie.hostOnly ? "host" : "domain"}\x00${cookie.path}\x00${cookie.name}`;
|
|
195
|
+
const previous = cookies.get(key);
|
|
196
|
+
const nextBytes = totalBytes - (previous === undefined ? 0 : cookieBytes(previous)) + cookieBytes(cookie);
|
|
197
|
+
if (nextBytes > MAX_COOKIE_BYTES) {
|
|
198
|
+
rejected += 1;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
cookies.set(key, cookie);
|
|
202
|
+
totalBytes = nextBytes;
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
cookies: [...cookies.values()].sort((left, right) => right.path.length - left.path.length || left.name.localeCompare(right.name) || left.domain.localeCompare(right.domain)),
|
|
206
|
+
rejected
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function jsonCookieArray(value) {
|
|
210
|
+
if (isUnknownArray(value))
|
|
211
|
+
return value;
|
|
212
|
+
return isRecord(value) && isUnknownArray(value.cookies) ? value.cookies : null;
|
|
213
|
+
}
|
|
214
|
+
function parseJson(input) {
|
|
215
|
+
try {
|
|
216
|
+
return jsonCookieArray(JSON.parse(input));
|
|
217
|
+
} catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function parseBase64Json(input) {
|
|
222
|
+
const compact = input.replace(/\s+/g, "");
|
|
223
|
+
if (compact === "" || compact.length > MAX_COOKIE_BYTES * 2 || !/^[a-z0-9+/]+=*$/i.test(compact))
|
|
224
|
+
return null;
|
|
225
|
+
try {
|
|
226
|
+
const decoded = Buffer.from(compact, "base64");
|
|
227
|
+
return decoded.byteLength > MAX_COOKIE_BYTES ? null : parseJson(decoded.toString("utf8"));
|
|
228
|
+
} catch {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function hasExplicitCookieScope(value) {
|
|
233
|
+
if (!isRecord(value))
|
|
234
|
+
return false;
|
|
235
|
+
return typeof value.domain === "string" && value.domain.trim() !== "" || typeof value.url === "string" && value.url.trim() !== "";
|
|
236
|
+
}
|
|
237
|
+
function parseNetscape(input) {
|
|
238
|
+
const cookies = [];
|
|
239
|
+
let looksLikeNetscape = /^# Netscape HTTP Cookie File/im.test(input);
|
|
240
|
+
let cursor = 0;
|
|
241
|
+
while (cursor <= input.length && cookies.length <= MAX_COOKIE_RECORDS) {
|
|
242
|
+
const newline = input.indexOf(`
|
|
243
|
+
`, cursor);
|
|
244
|
+
const lineEnd = newline === -1 ? input.length : newline;
|
|
245
|
+
const rawLine = input.slice(cursor, lineEnd).replace(/\r$/, "");
|
|
246
|
+
cursor = newline === -1 ? input.length + 1 : newline + 1;
|
|
247
|
+
const line = rawLine.trim();
|
|
248
|
+
if (line === "" || line.startsWith("#") && !line.startsWith("#HttpOnly_"))
|
|
249
|
+
continue;
|
|
250
|
+
if (line.length > 80 * 1024)
|
|
251
|
+
continue;
|
|
252
|
+
const columns = line.split("\t", 8);
|
|
253
|
+
if (columns.length < 7)
|
|
254
|
+
continue;
|
|
255
|
+
looksLikeNetscape = true;
|
|
256
|
+
const rawDomain = columns[0];
|
|
257
|
+
const includeSubdomains = columns[1];
|
|
258
|
+
const path = columns[2];
|
|
259
|
+
const secure = columns[3];
|
|
260
|
+
const rawExpires = columns[4];
|
|
261
|
+
const name = columns[5];
|
|
262
|
+
const value = columns.slice(6).join("\t");
|
|
263
|
+
if (rawDomain === undefined || includeSubdomains === undefined || path === undefined || secure === undefined || rawExpires === undefined || name === undefined)
|
|
264
|
+
continue;
|
|
265
|
+
const httpOnly = rawDomain.startsWith("#HttpOnly_");
|
|
266
|
+
const domain = httpOnly ? rawDomain.slice("#HttpOnly_".length) : rawDomain;
|
|
267
|
+
const expires = Number(rawExpires);
|
|
268
|
+
cookies.push({
|
|
269
|
+
name,
|
|
270
|
+
value,
|
|
271
|
+
domain,
|
|
272
|
+
hostOnly: includeSubdomains.toUpperCase() !== "TRUE",
|
|
273
|
+
path,
|
|
274
|
+
secure: secure.toUpperCase() === "TRUE",
|
|
275
|
+
httpOnly,
|
|
276
|
+
...Number.isFinite(expires) && expires > 0 ? { expires } : {}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return looksLikeNetscape ? cookies : null;
|
|
280
|
+
}
|
|
281
|
+
function unquote(value) {
|
|
282
|
+
const trimmed = value.trim();
|
|
283
|
+
const first = trimmed[0];
|
|
284
|
+
return (first === "'" || first === '"') && trimmed.at(-1) === first ? trimmed.slice(1, -1) : trimmed;
|
|
285
|
+
}
|
|
286
|
+
function parseCookieHeaderValue(value, target) {
|
|
287
|
+
const cookies = [];
|
|
288
|
+
const restrictivePath = target.pathname === "" ? "/" : target.pathname;
|
|
289
|
+
let cursor = 0;
|
|
290
|
+
while (cursor <= value.length && cookies.length <= MAX_COOKIE_RECORDS) {
|
|
291
|
+
const delimiter = value.indexOf(";", cursor);
|
|
292
|
+
const pairEnd = delimiter === -1 ? value.length : delimiter;
|
|
293
|
+
const pair = value.slice(cursor, pairEnd);
|
|
294
|
+
cursor = delimiter === -1 ? value.length + 1 : delimiter + 1;
|
|
295
|
+
const separator = pair.indexOf("=");
|
|
296
|
+
if (separator < 1)
|
|
297
|
+
continue;
|
|
298
|
+
cookies.push({
|
|
299
|
+
name: pair.slice(0, separator).trim(),
|
|
300
|
+
value: pair.slice(separator + 1).trim(),
|
|
301
|
+
domain: target.hostname,
|
|
302
|
+
hostOnly: true,
|
|
303
|
+
path: restrictivePath,
|
|
304
|
+
secure: target.protocol === "https:",
|
|
305
|
+
httpOnly: true,
|
|
306
|
+
sameSite: "Strict"
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return cookies;
|
|
310
|
+
}
|
|
311
|
+
function curlCookieValue(input) {
|
|
312
|
+
const patterns = [
|
|
313
|
+
/(?:^|\s)(?:-b|--cookie)(?:=|\s+)(('[^']*')|("[^"]*")|[^\s]+)/i,
|
|
314
|
+
/(?:^|\s)(?:-H|--header)(?:=|\s+)(('Cookie:\s*[^']*')|("Cookie:\s*[^"]*"))/i
|
|
315
|
+
];
|
|
316
|
+
for (const pattern of patterns) {
|
|
317
|
+
const raw = pattern.exec(input)?.[1];
|
|
318
|
+
if (raw === undefined)
|
|
319
|
+
continue;
|
|
320
|
+
return { value: unquote(raw).replace(/^Cookie:\s*/i, ""), curl: true };
|
|
321
|
+
}
|
|
322
|
+
const header = /^Cookie:\s*([^\r\n]*)$/im.exec(input)?.[1];
|
|
323
|
+
if (header !== undefined)
|
|
324
|
+
return { value: header, curl: false };
|
|
325
|
+
const trimmed = input.trim();
|
|
326
|
+
return !trimmed.includes(`
|
|
327
|
+
`) && trimmed.includes("=") ? { value: trimmed, curl: false } : null;
|
|
328
|
+
}
|
|
329
|
+
function parseCookiePayload(input, target, nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
330
|
+
if (Buffer.byteLength(input, "utf8") > MAX_COOKIE_BYTES)
|
|
331
|
+
return { ok: false, reason: "too-large" };
|
|
332
|
+
if (input.trim() === "")
|
|
333
|
+
return { ok: false, reason: "empty" };
|
|
334
|
+
let values = parseJson(input);
|
|
335
|
+
let format = "json";
|
|
336
|
+
if (values === null) {
|
|
337
|
+
values = parseBase64Json(input);
|
|
338
|
+
format = "base64-json";
|
|
339
|
+
}
|
|
340
|
+
if (values === null) {
|
|
341
|
+
values = parseNetscape(input);
|
|
342
|
+
format = "netscape";
|
|
343
|
+
}
|
|
344
|
+
if (values === null) {
|
|
345
|
+
const header = curlCookieValue(input);
|
|
346
|
+
if (header !== null) {
|
|
347
|
+
values = parseCookieHeaderValue(header.value, target);
|
|
348
|
+
format = header.curl ? "curl" : "cookie-header";
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (values === null)
|
|
352
|
+
return { ok: false, reason: "invalid" };
|
|
353
|
+
const scopeProvenance = format === "netscape" || (format === "json" || format === "base64-json") && values.every(hasExplicitCookieScope) ? "explicit" : "target-inferred";
|
|
354
|
+
const filtered = filterCookies(values, target, nowSeconds);
|
|
355
|
+
return filtered.cookies.length === 0 ? { ok: false, reason: "empty" } : { ok: true, format, scopeProvenance, ...filtered };
|
|
356
|
+
}
|
|
357
|
+
function readCookieFile(path, target, options = {}) {
|
|
358
|
+
let descriptor;
|
|
359
|
+
try {
|
|
360
|
+
const absolute = resolve(path);
|
|
361
|
+
const noFollow = "O_NOFOLLOW" in constants ? constants.O_NOFOLLOW : 0;
|
|
362
|
+
const nonBlocking = "O_NONBLOCK" in constants ? constants.O_NONBLOCK : 0;
|
|
363
|
+
descriptor = openSync(absolute, constants.O_RDONLY | noFollow | nonBlocking);
|
|
364
|
+
} catch {
|
|
365
|
+
return { ok: false, reason: "unavailable" };
|
|
366
|
+
}
|
|
367
|
+
try {
|
|
368
|
+
options.afterOpen?.();
|
|
369
|
+
const stats = fstatSync(descriptor);
|
|
370
|
+
if (!stats.isFile())
|
|
371
|
+
return { ok: false, reason: "unavailable" };
|
|
372
|
+
if (options.requirePrivate === true && ((stats.mode & 63) !== 0 || typeof process.getuid === "function" && stats.uid !== process.getuid()))
|
|
373
|
+
return { ok: false, reason: "unsafe-permissions" };
|
|
374
|
+
if (stats.size > MAX_COOKIE_BYTES)
|
|
375
|
+
return { ok: false, reason: "too-large" };
|
|
376
|
+
const chunks = [];
|
|
377
|
+
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
378
|
+
let total = 0;
|
|
379
|
+
for (;; ) {
|
|
380
|
+
const count = readSync(descriptor, buffer, 0, buffer.byteLength, null);
|
|
381
|
+
if (count === 0)
|
|
382
|
+
break;
|
|
383
|
+
total += count;
|
|
384
|
+
if (total > MAX_COOKIE_BYTES)
|
|
385
|
+
return { ok: false, reason: "too-large" };
|
|
386
|
+
chunks.push(Buffer.from(buffer.subarray(0, count)));
|
|
387
|
+
}
|
|
388
|
+
let text;
|
|
389
|
+
try {
|
|
390
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks, total));
|
|
391
|
+
} catch {
|
|
392
|
+
return { ok: false, reason: "invalid" };
|
|
393
|
+
}
|
|
394
|
+
return parseCookiePayload(text, target);
|
|
395
|
+
} catch {
|
|
396
|
+
return { ok: false, reason: "unavailable" };
|
|
397
|
+
} finally {
|
|
398
|
+
closeSync(descriptor);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function filterCookieProviderResult(value, target) {
|
|
402
|
+
if (!isRecord(value) || !Array.isArray(value.cookies)) {
|
|
403
|
+
return { validShape: false, cookies: [], rejected: 0, providerWarningCount: 0 };
|
|
404
|
+
}
|
|
405
|
+
const provenancePreserving = value.cookies.filter((cookie) => isRecord(cookie) && typeof cookie.hostOnly === "boolean");
|
|
406
|
+
const missingProvenance = value.cookies.length - provenancePreserving.length;
|
|
407
|
+
const filtered = filterCookies(provenancePreserving, target, Math.floor(Date.now() / 1000));
|
|
408
|
+
return {
|
|
409
|
+
validShape: true,
|
|
410
|
+
...filtered,
|
|
411
|
+
rejected: filtered.rejected + missingProvenance,
|
|
412
|
+
providerWarningCount: Array.isArray(value.warnings) ? value.warnings.length : 0
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function renderCookieHeader(cookies) {
|
|
416
|
+
return cookies.map(({ name, value }) => `${name}=${value}`).join("; ");
|
|
417
|
+
}
|
|
418
|
+
function renderNetscapeCookieJar(cookies, target) {
|
|
419
|
+
const hostname = target.hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
420
|
+
return [
|
|
421
|
+
"# Netscape HTTP Cookie File",
|
|
422
|
+
"# Created temporarily by kb clip; deleted after media capture.",
|
|
423
|
+
...cookies.map((cookie) => {
|
|
424
|
+
const domain = `${cookie.httpOnly ? "#HttpOnly_" : ""}${hostname}`;
|
|
425
|
+
return `${domain} FALSE ${cookie.path} ${cookie.secure ? "TRUE" : "FALSE"} ${cookie.expires} ${cookie.name} ${cookie.value}`;
|
|
426
|
+
}),
|
|
427
|
+
""
|
|
428
|
+
].join(`
|
|
429
|
+
`);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export { MAX_COOKIE_RECORDS, MAX_COOKIE_BYTES, filterCookies, parseCookiePayload, readCookieFile, filterCookieProviderResult, renderCookieHeader, renderNetscapeCookieJar };
|