altweb-context 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/altweb-context.mjs +165 -26
- package/package.json +3 -3
package/dist/altweb-context.mjs
CHANGED
|
@@ -6920,7 +6920,22 @@ if (typeof globalThis.window === "undefined") {
|
|
|
6920
6920
|
}
|
|
6921
6921
|
|
|
6922
6922
|
// ../core/src/crypto/encoding.ts
|
|
6923
|
+
var BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
6923
6924
|
function base64urlDecode(str) {
|
|
6925
|
+
if (!/^[A-Za-z0-9_-]*$/.test(str)) {
|
|
6926
|
+
throw new Error("invalid base64url charset");
|
|
6927
|
+
}
|
|
6928
|
+
const rem = str.length % 4;
|
|
6929
|
+
if (rem === 1) {
|
|
6930
|
+
throw new Error("invalid base64url length");
|
|
6931
|
+
}
|
|
6932
|
+
if (rem !== 0) {
|
|
6933
|
+
const lastValue = BASE64URL_ALPHABET.indexOf(str[str.length - 1]);
|
|
6934
|
+
const unusedBits = rem === 2 ? 15 : 3;
|
|
6935
|
+
if ((lastValue & unusedBits) !== 0) {
|
|
6936
|
+
throw new Error("non-canonical base64url encoding");
|
|
6937
|
+
}
|
|
6938
|
+
}
|
|
6924
6939
|
let base644 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
6925
6940
|
while (base644.length % 4) base644 += "=";
|
|
6926
6941
|
const binary = atob(base644);
|
|
@@ -6972,10 +6987,25 @@ async function decrypt(payload, password) {
|
|
|
6972
6987
|
}
|
|
6973
6988
|
|
|
6974
6989
|
// ../core/src/crypto/signing.ts
|
|
6990
|
+
var P256_N = BigInt(
|
|
6991
|
+
"0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"
|
|
6992
|
+
);
|
|
6993
|
+
var P256_HALF_N = P256_N >> 1n;
|
|
6994
|
+
function isLowS(signature) {
|
|
6995
|
+
if (signature.length !== 64) return true;
|
|
6996
|
+
let s = 0n;
|
|
6997
|
+
for (let i = 32; i < 64; i++) {
|
|
6998
|
+
s = s << 8n | BigInt(signature[i]);
|
|
6999
|
+
}
|
|
7000
|
+
return s <= P256_HALF_N;
|
|
7001
|
+
}
|
|
6975
7002
|
async function verify(data, signatureBase64, publicKeyBase64) {
|
|
6976
7003
|
try {
|
|
6977
7004
|
const signature = base64urlDecode(signatureBase64);
|
|
6978
7005
|
const publicKeyData = base64urlDecode(publicKeyBase64);
|
|
7006
|
+
if (!isLowS(signature)) {
|
|
7007
|
+
return false;
|
|
7008
|
+
}
|
|
6979
7009
|
const publicKey = await crypto.subtle.importKey(
|
|
6980
7010
|
"spki",
|
|
6981
7011
|
publicKeyData,
|
|
@@ -11196,30 +11226,39 @@ var pako = {
|
|
|
11196
11226
|
|
|
11197
11227
|
// ../core/src/compression/compress.ts
|
|
11198
11228
|
var MAX_DECOMPRESSED_BYTES = 16 * 1024 * 1024;
|
|
11229
|
+
var MAX_COMPRESSED_BYTES = 17 * 1024 * 1024;
|
|
11230
|
+
var INFLATE_SLICE_BYTES = 64 * 1024;
|
|
11199
11231
|
function decompress(data) {
|
|
11232
|
+
if (data.length > MAX_COMPRESSED_BYTES) {
|
|
11233
|
+
throw new Error(
|
|
11234
|
+
`Compressed payload exceeds the ${MAX_COMPRESSED_BYTES} byte limit`
|
|
11235
|
+
);
|
|
11236
|
+
}
|
|
11200
11237
|
const inflator = new pako.Inflate();
|
|
11201
11238
|
const chunks = [];
|
|
11202
11239
|
let total = 0;
|
|
11203
|
-
let bombed = false;
|
|
11204
11240
|
inflator.onData = (chunk) => {
|
|
11205
11241
|
const bytes = typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);
|
|
11206
11242
|
total += bytes.length;
|
|
11207
|
-
if (total > MAX_DECOMPRESSED_BYTES) {
|
|
11208
|
-
bombed = true;
|
|
11209
|
-
inflator.err = -2;
|
|
11210
|
-
return;
|
|
11211
|
-
}
|
|
11212
11243
|
chunks.push(bytes);
|
|
11213
11244
|
};
|
|
11214
|
-
|
|
11215
|
-
|
|
11216
|
-
inflator.push(data, true);
|
|
11217
|
-
if (bombed) {
|
|
11218
|
-
throw new Error(
|
|
11219
|
-
`Decompressed payload exceeds the ${MAX_DECOMPRESSED_BYTES} byte limit`
|
|
11220
|
-
);
|
|
11245
|
+
if (data.length === 0) {
|
|
11246
|
+
inflator.push(data, true);
|
|
11221
11247
|
}
|
|
11222
|
-
|
|
11248
|
+
for (let offset2 = 0; offset2 < data.length; offset2 += INFLATE_SLICE_BYTES) {
|
|
11249
|
+
const last = offset2 + INFLATE_SLICE_BYTES >= data.length;
|
|
11250
|
+
inflator.push(data.subarray(offset2, offset2 + INFLATE_SLICE_BYTES), last);
|
|
11251
|
+
if (total > MAX_DECOMPRESSED_BYTES) {
|
|
11252
|
+
throw new Error(
|
|
11253
|
+
`Decompressed payload exceeds the ${MAX_DECOMPRESSED_BYTES} byte limit`
|
|
11254
|
+
);
|
|
11255
|
+
}
|
|
11256
|
+
if (inflator.err) {
|
|
11257
|
+
throw new Error(inflator.msg || "invalid deflate stream");
|
|
11258
|
+
}
|
|
11259
|
+
if (inflator.ended) break;
|
|
11260
|
+
}
|
|
11261
|
+
if (inflator.err || !inflator.ended) {
|
|
11223
11262
|
throw new Error(inflator.msg || "invalid deflate stream");
|
|
11224
11263
|
}
|
|
11225
11264
|
const result = new Uint8Array(total);
|
|
@@ -12775,12 +12814,45 @@ function sanitizePage(page) {
|
|
|
12775
12814
|
...page.meta,
|
|
12776
12815
|
title: purify.sanitize(page.meta.title, { ALLOWED_TAGS: [] }),
|
|
12777
12816
|
description: page.meta.description ? purify.sanitize(page.meta.description, { ALLOWED_TAGS: [] }) : void 0,
|
|
12778
|
-
author: page.meta.author ? purify.sanitize(page.meta.author, { ALLOWED_TAGS: [] }) : void 0
|
|
12817
|
+
author: page.meta.author ? purify.sanitize(page.meta.author, { ALLOWED_TAGS: [] }) : void 0,
|
|
12818
|
+
header: page.meta.header ? sanitizeHeader(page.meta.header) : void 0,
|
|
12819
|
+
footer: page.meta.footer ? sanitizeFooter(page.meta.footer) : void 0
|
|
12779
12820
|
},
|
|
12780
12821
|
blocks: page.blocks.map(sanitizeBlock),
|
|
12781
12822
|
style: sanitizeStyle(page.style)
|
|
12782
12823
|
};
|
|
12783
12824
|
}
|
|
12825
|
+
function sanitizeHeader(header) {
|
|
12826
|
+
return {
|
|
12827
|
+
...header,
|
|
12828
|
+
logo: header.logo ? sanitizeImageSrc(header.logo) : void 0,
|
|
12829
|
+
customText: header.customText ? purify.sanitize(header.customText, { ALLOWED_TAGS: [] }) : void 0
|
|
12830
|
+
};
|
|
12831
|
+
}
|
|
12832
|
+
function sanitizeFooter(footer) {
|
|
12833
|
+
return {
|
|
12834
|
+
...footer,
|
|
12835
|
+
copyright: footer.copyright ? purify.sanitize(footer.copyright, { ALLOWED_TAGS: [] }) : void 0,
|
|
12836
|
+
customText: footer.customText ? purify.sanitize(footer.customText, { ALLOWED_TAGS: [] }) : void 0,
|
|
12837
|
+
links: footer.links ? footer.links.map((link) => ({
|
|
12838
|
+
label: purify.sanitize(link.label, { ALLOWED_TAGS: [] }),
|
|
12839
|
+
url: sanitizeUrl(link.url)
|
|
12840
|
+
})) : void 0
|
|
12841
|
+
};
|
|
12842
|
+
}
|
|
12843
|
+
function sanitizeImageSrc(src) {
|
|
12844
|
+
if (src.startsWith("data:")) {
|
|
12845
|
+
return validateDataUri(src);
|
|
12846
|
+
}
|
|
12847
|
+
try {
|
|
12848
|
+
const parsed = new URL(src);
|
|
12849
|
+
if (["http:", "https:"].includes(parsed.protocol)) {
|
|
12850
|
+
return src;
|
|
12851
|
+
}
|
|
12852
|
+
} catch {
|
|
12853
|
+
}
|
|
12854
|
+
return "";
|
|
12855
|
+
}
|
|
12784
12856
|
function sanitizeBlock(block) {
|
|
12785
12857
|
switch (block.t) {
|
|
12786
12858
|
case "h":
|
|
@@ -27590,6 +27662,14 @@ function validatePageStructure(data) {
|
|
|
27590
27662
|
}
|
|
27591
27663
|
|
|
27592
27664
|
// ../core/src/codec/decoder.ts
|
|
27665
|
+
var MAX_ENVELOPE_CHARS = 24 * 1024 * 1024;
|
|
27666
|
+
function assertEnvelopeSize(hash2) {
|
|
27667
|
+
if (hash2.length > MAX_ENVELOPE_CHARS) {
|
|
27668
|
+
throw new ValidationError(
|
|
27669
|
+
`Envelope exceeds the ${MAX_ENVELOPE_CHARS} character limit`
|
|
27670
|
+
);
|
|
27671
|
+
}
|
|
27672
|
+
}
|
|
27593
27673
|
var DecryptionError = class extends Error {
|
|
27594
27674
|
constructor(message) {
|
|
27595
27675
|
super(message);
|
|
@@ -27603,6 +27683,7 @@ var ValidationError = class extends Error {
|
|
|
27603
27683
|
}
|
|
27604
27684
|
};
|
|
27605
27685
|
async function decodePage(hash2, password) {
|
|
27686
|
+
assertEnvelopeSize(hash2);
|
|
27606
27687
|
const envelopeBytes = base64urlDecode(hash2);
|
|
27607
27688
|
const envelopeJson = new TextDecoder().decode(envelopeBytes);
|
|
27608
27689
|
let envelope;
|
|
@@ -27713,6 +27794,7 @@ async function decodePage(hash2, password) {
|
|
|
27713
27794
|
}
|
|
27714
27795
|
function isEncryptedContent(hash2) {
|
|
27715
27796
|
try {
|
|
27797
|
+
assertEnvelopeSize(hash2);
|
|
27716
27798
|
const envelopeBytes = base64urlDecode(hash2);
|
|
27717
27799
|
const envelopeJson = new TextDecoder().decode(envelopeBytes);
|
|
27718
27800
|
const envelope = JSON.parse(envelopeJson);
|
|
@@ -27724,6 +27806,9 @@ function isEncryptedContent(hash2) {
|
|
|
27724
27806
|
|
|
27725
27807
|
// ../core/src/codec/inspect.ts
|
|
27726
27808
|
async function inspectArtifact(hash2) {
|
|
27809
|
+
if (hash2.length > MAX_ENVELOPE_CHARS) {
|
|
27810
|
+
throw new ValidationError("Invalid artifact format");
|
|
27811
|
+
}
|
|
27727
27812
|
let envelope;
|
|
27728
27813
|
try {
|
|
27729
27814
|
envelope = JSON.parse(new TextDecoder().decode(base64urlDecode(hash2)));
|
|
@@ -29089,10 +29174,28 @@ var HTML_ENTITIES = {
|
|
|
29089
29174
|
function decodeHtmlEntities(text2) {
|
|
29090
29175
|
return text2.replace(/&(?:amp|lt|gt|quot|#39|apos|nbsp);/g, (match) => HTML_ENTITIES[match] || match);
|
|
29091
29176
|
}
|
|
29177
|
+
function stripTags(html2) {
|
|
29178
|
+
let out = "";
|
|
29179
|
+
let i = 0;
|
|
29180
|
+
while (i < html2.length) {
|
|
29181
|
+
const lt = html2.indexOf("<", i);
|
|
29182
|
+
if (lt === -1) {
|
|
29183
|
+
out += html2.slice(i);
|
|
29184
|
+
break;
|
|
29185
|
+
}
|
|
29186
|
+
out += html2.slice(i, lt);
|
|
29187
|
+
const gt = html2.indexOf(">", lt + 1);
|
|
29188
|
+
if (gt === -1) {
|
|
29189
|
+
out += html2.slice(lt);
|
|
29190
|
+
break;
|
|
29191
|
+
}
|
|
29192
|
+
i = gt + 1;
|
|
29193
|
+
}
|
|
29194
|
+
return out;
|
|
29195
|
+
}
|
|
29092
29196
|
function stripHtml(html2) {
|
|
29093
29197
|
if (!html2) return "";
|
|
29094
|
-
|
|
29095
|
-
return decodeHtmlEntities(stripped);
|
|
29198
|
+
return decodeHtmlEntities(stripTags(html2));
|
|
29096
29199
|
}
|
|
29097
29200
|
function htmlToMarkdown(html2) {
|
|
29098
29201
|
if (!html2) return "";
|
|
@@ -29101,7 +29204,7 @@ function htmlToMarkdown(html2) {
|
|
|
29101
29204
|
md = md.replace(/<(?:em|i)>(.*?)<\/(?:em|i)>/gi, "*$1*");
|
|
29102
29205
|
md = md.replace(/<code>(.*?)<\/code>/gi, "`$1`");
|
|
29103
29206
|
md = md.replace(/<a\s+href="([^"]*)"[^>]*>(.*?)<\/a>/gi, "[$2]($1)");
|
|
29104
|
-
md = md
|
|
29207
|
+
md = stripTags(md);
|
|
29105
29208
|
return decodeHtmlEntities(md);
|
|
29106
29209
|
}
|
|
29107
29210
|
|
|
@@ -47385,16 +47488,52 @@ function isPrivateV4(addr) {
|
|
|
47385
47488
|
a === 169 && b2 === 254 || // link-local / cloud metadata
|
|
47386
47489
|
a === 172 && b2 >= 16 && b2 <= 31 || a === 192 && b2 === 168;
|
|
47387
47490
|
}
|
|
47491
|
+
function parseHextets(addr) {
|
|
47492
|
+
let s = addr;
|
|
47493
|
+
const lastColon = s.lastIndexOf(":");
|
|
47494
|
+
const tail = s.slice(lastColon + 1);
|
|
47495
|
+
if (tail.includes(".")) {
|
|
47496
|
+
if (isIP(tail) !== 4) return null;
|
|
47497
|
+
const [a, b2, c, d] = tail.split(".").map(Number);
|
|
47498
|
+
s = s.slice(0, lastColon + 1) + (a << 8 | b2).toString(16) + ":" + (c << 8 | d).toString(16);
|
|
47499
|
+
}
|
|
47500
|
+
const doubleColons = s.split("::");
|
|
47501
|
+
if (doubleColons.length > 2) return null;
|
|
47502
|
+
const parseGroups = (part) => {
|
|
47503
|
+
if (part === "") return [];
|
|
47504
|
+
const groups = [];
|
|
47505
|
+
for (const g2 of part.split(":")) {
|
|
47506
|
+
if (!/^[0-9a-fA-F]{1,4}$/.test(g2)) return null;
|
|
47507
|
+
groups.push(parseInt(g2, 16));
|
|
47508
|
+
}
|
|
47509
|
+
return groups;
|
|
47510
|
+
};
|
|
47511
|
+
const head = parseGroups(doubleColons[0]);
|
|
47512
|
+
const tailGroups = doubleColons.length === 2 ? parseGroups(doubleColons[1]) : [];
|
|
47513
|
+
if (head === null || tailGroups === null) return null;
|
|
47514
|
+
if (doubleColons.length === 2) {
|
|
47515
|
+
const fill = 8 - head.length - tailGroups.length;
|
|
47516
|
+
if (fill < 1) return null;
|
|
47517
|
+
return [...head, ...new Array(fill).fill(0), ...tailGroups];
|
|
47518
|
+
}
|
|
47519
|
+
return head.length === 8 ? head : null;
|
|
47520
|
+
}
|
|
47521
|
+
function embeddedV4(hi, lo) {
|
|
47522
|
+
return `${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`;
|
|
47523
|
+
}
|
|
47388
47524
|
function isPrivateAddress(addr) {
|
|
47389
|
-
const lower = addr.toLowerCase();
|
|
47390
|
-
if (lower.startsWith("::ffff:")) {
|
|
47391
|
-
const embedded = lower.slice(7);
|
|
47392
|
-
return isIP(embedded) === 4 ? isPrivateV4(embedded) : true;
|
|
47393
|
-
}
|
|
47394
47525
|
if (isIP(addr) === 4) return isPrivateV4(addr);
|
|
47395
|
-
|
|
47396
|
-
|
|
47397
|
-
|
|
47526
|
+
const h = parseHextets(addr);
|
|
47527
|
+
if (h === null) return true;
|
|
47528
|
+
if (h.slice(0, 6).every((x2) => x2 === 0)) return true;
|
|
47529
|
+
if (h.slice(0, 5).every((x2) => x2 === 0) && h[5] === 65535) {
|
|
47530
|
+
return isPrivateV4(embeddedV4(h[6], h[7]));
|
|
47531
|
+
}
|
|
47532
|
+
if ((h[0] & 65472) === 65152) return true;
|
|
47533
|
+
if ((h[0] & 65024) === 64512) return true;
|
|
47534
|
+
if (h[0] === 100 && h[1] === 65435) return true;
|
|
47535
|
+
if (h[0] === 8194) return true;
|
|
47536
|
+
return false;
|
|
47398
47537
|
}
|
|
47399
47538
|
function guardedLookup(hostname4, options, callback) {
|
|
47400
47539
|
lookup(hostname4, { ...options, all: true }, (err2, addresses) => {
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "altweb-context",
|
|
3
3
|
"mcpName": "software.altweb/altweb-context",
|
|
4
|
-
"version": "1.0
|
|
5
|
-
"description": "MCP server that loads ALTWEB signed context capsules into AI agents
|
|
4
|
+
"version": "1.1.0",
|
|
5
|
+
"description": "MCP server that loads ALTWEB signed context capsules into AI agents \u2014 verifies first, refuses unsigned, tampered, or untrusted context by default. Verify before you inject.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "AGPL-3.0-or-later",
|
|
8
|
-
"author": "Daniel C.
|
|
8
|
+
"author": "Daniel C. \u0218OIMU",
|
|
9
9
|
"homepage": "https://altweb.software",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|