@agentcash/discovery 1.1.2 → 1.2.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/cli.cjs +190 -21
- package/dist/cli.js +190 -21
- package/dist/index.cjs +197 -20
- package/dist/index.d.cts +45 -3
- package/dist/index.d.ts +45 -3
- package/dist/index.js +196 -20
- package/dist/schemas.cjs +0 -1
- package/dist/schemas.js +0 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -13864,7 +13864,6 @@ var WellKnownDocSchema = external_exports.object({
|
|
|
13864
13864
|
version: external_exports.number().optional(),
|
|
13865
13865
|
resources: external_exports.array(external_exports.string()).default([]),
|
|
13866
13866
|
mppResources: external_exports.array(external_exports.string()).optional(),
|
|
13867
|
-
// isMmmEnabled
|
|
13868
13867
|
description: external_exports.string().optional(),
|
|
13869
13868
|
ownershipProofs: external_exports.array(external_exports.string()).optional(),
|
|
13870
13869
|
instructions: external_exports.string().optional()
|
|
@@ -13974,9 +13973,6 @@ function fetchSafe(url2, init) {
|
|
|
13974
13973
|
return import_neverthrow.ResultAsync.fromPromise(fetch(url2, init), toFetchError);
|
|
13975
13974
|
}
|
|
13976
13975
|
|
|
13977
|
-
// src/mmm-enabled.ts
|
|
13978
|
-
var isMmmEnabled = () => "1.1.2".includes("-mmm");
|
|
13979
|
-
|
|
13980
13976
|
// src/core/source/openapi/index.ts
|
|
13981
13977
|
var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
13982
13978
|
const routes = [];
|
|
@@ -13986,9 +13982,7 @@ var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
|
13986
13982
|
if (!operation) continue;
|
|
13987
13983
|
const authMode = inferAuthMode(operation, doc.security, doc.components?.securitySchemes) ?? void 0;
|
|
13988
13984
|
const p = operation["x-payment-info"];
|
|
13989
|
-
const protocols = (p?.protocols ?? []).filter(
|
|
13990
|
-
(proto) => proto !== "mpp" || isMmmEnabled()
|
|
13991
|
-
);
|
|
13985
|
+
const protocols = (p?.protocols ?? []).filter((proto) => proto.length > 0);
|
|
13992
13986
|
const pricing = (authMode === "paid" || authMode === "apiKey+paid") && p ? {
|
|
13993
13987
|
pricingMode: p.pricingMode,
|
|
13994
13988
|
...p.price ? { price: p.price } : {},
|
|
@@ -14102,6 +14096,7 @@ function checkL2ForOpenAPI(openApi) {
|
|
|
14102
14096
|
return {
|
|
14103
14097
|
...openApi.info.title ? { title: openApi.info.title } : {},
|
|
14104
14098
|
...openApi.info.description ? { description: openApi.info.description } : {},
|
|
14099
|
+
...openApi.info.version ? { version: openApi.info.version } : {},
|
|
14105
14100
|
routes,
|
|
14106
14101
|
source: "openapi"
|
|
14107
14102
|
};
|
|
@@ -14151,9 +14146,23 @@ var AUDIT_CODES = {
|
|
|
14151
14146
|
L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
|
|
14152
14147
|
L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
|
|
14153
14148
|
L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
|
|
14149
|
+
L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
|
|
14154
14150
|
// ─── L4 guidance checks ──────────────────────────────────────────────────────
|
|
14155
14151
|
L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
|
|
14156
|
-
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
|
|
14152
|
+
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
|
|
14153
|
+
// ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
|
|
14154
|
+
MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
|
|
14155
|
+
MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
|
|
14156
|
+
MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
|
|
14157
|
+
MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
|
|
14158
|
+
MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
|
|
14159
|
+
MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
|
|
14160
|
+
MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
|
|
14161
|
+
MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
|
|
14162
|
+
MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
|
|
14163
|
+
MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
|
|
14164
|
+
MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
|
|
14165
|
+
MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
|
|
14157
14166
|
};
|
|
14158
14167
|
|
|
14159
14168
|
// src/audit/warnings/sources.ts
|
|
@@ -14732,6 +14741,157 @@ function validateWithCoinbaseSchema2(body) {
|
|
|
14732
14741
|
});
|
|
14733
14742
|
}
|
|
14734
14743
|
|
|
14744
|
+
// src/audit/warnings/mpp.ts
|
|
14745
|
+
function parseAuthParams(segment) {
|
|
14746
|
+
const params = {};
|
|
14747
|
+
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
14748
|
+
let match;
|
|
14749
|
+
while ((match = re.exec(segment)) !== null) {
|
|
14750
|
+
params[match[1]] = match[2] ?? match[3] ?? "";
|
|
14751
|
+
}
|
|
14752
|
+
return params;
|
|
14753
|
+
}
|
|
14754
|
+
function getWarningsForMppHeader(wwwAuthenticate) {
|
|
14755
|
+
if (!wwwAuthenticate?.trim()) {
|
|
14756
|
+
return [
|
|
14757
|
+
{
|
|
14758
|
+
code: AUDIT_CODES.MPP_HEADER_MISSING,
|
|
14759
|
+
severity: "error",
|
|
14760
|
+
message: "WWW-Authenticate header is absent.",
|
|
14761
|
+
hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
|
|
14762
|
+
}
|
|
14763
|
+
];
|
|
14764
|
+
}
|
|
14765
|
+
const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
|
|
14766
|
+
if (segments.length === 0) {
|
|
14767
|
+
return [
|
|
14768
|
+
{
|
|
14769
|
+
code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
|
|
14770
|
+
severity: "error",
|
|
14771
|
+
message: "WWW-Authenticate header contains no Payment challenges.",
|
|
14772
|
+
hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
|
|
14773
|
+
}
|
|
14774
|
+
];
|
|
14775
|
+
}
|
|
14776
|
+
const warnings = [];
|
|
14777
|
+
for (let i = 0; i < segments.length; i++) {
|
|
14778
|
+
const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
|
|
14779
|
+
const params = parseAuthParams(stripped);
|
|
14780
|
+
const idx = `WWW-Authenticate[${i}]`;
|
|
14781
|
+
if (!params["id"]) {
|
|
14782
|
+
warnings.push({
|
|
14783
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
|
|
14784
|
+
severity: "error",
|
|
14785
|
+
message: `Payment challenge ${i} is missing the id parameter.`,
|
|
14786
|
+
hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
|
|
14787
|
+
path: `${idx}.id`
|
|
14788
|
+
});
|
|
14789
|
+
}
|
|
14790
|
+
if (!params["method"]) {
|
|
14791
|
+
warnings.push({
|
|
14792
|
+
code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
|
|
14793
|
+
severity: "error",
|
|
14794
|
+
message: `Payment challenge ${i} is missing the method parameter.`,
|
|
14795
|
+
hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
|
|
14796
|
+
path: `${idx}.method`
|
|
14797
|
+
});
|
|
14798
|
+
}
|
|
14799
|
+
if (!params["intent"]) {
|
|
14800
|
+
warnings.push({
|
|
14801
|
+
code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
|
|
14802
|
+
severity: "error",
|
|
14803
|
+
message: `Payment challenge ${i} is missing the intent parameter.`,
|
|
14804
|
+
hint: 'Set intent="charge" on the Payment challenge.',
|
|
14805
|
+
path: `${idx}.intent`
|
|
14806
|
+
});
|
|
14807
|
+
}
|
|
14808
|
+
if (!params["realm"]) {
|
|
14809
|
+
warnings.push({
|
|
14810
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
|
|
14811
|
+
severity: "error",
|
|
14812
|
+
message: `Payment challenge ${i} is missing the realm parameter.`,
|
|
14813
|
+
hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
|
|
14814
|
+
path: `${idx}.realm`
|
|
14815
|
+
});
|
|
14816
|
+
}
|
|
14817
|
+
if (!params["expires"]) {
|
|
14818
|
+
warnings.push({
|
|
14819
|
+
code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
|
|
14820
|
+
severity: "error",
|
|
14821
|
+
message: `Payment challenge ${i} is missing the expires parameter.`,
|
|
14822
|
+
hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
|
|
14823
|
+
path: `${idx}.expires`
|
|
14824
|
+
});
|
|
14825
|
+
}
|
|
14826
|
+
const requestStr = params["request"];
|
|
14827
|
+
if (!requestStr) {
|
|
14828
|
+
warnings.push({
|
|
14829
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
|
|
14830
|
+
severity: "error",
|
|
14831
|
+
message: `Payment challenge ${i} is missing the request field.`,
|
|
14832
|
+
hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
|
|
14833
|
+
path: `${idx}.request`
|
|
14834
|
+
});
|
|
14835
|
+
continue;
|
|
14836
|
+
}
|
|
14837
|
+
let request;
|
|
14838
|
+
try {
|
|
14839
|
+
const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
|
|
14840
|
+
const parsed = JSON.parse(decoded);
|
|
14841
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
14842
|
+
throw new Error("not an object");
|
|
14843
|
+
}
|
|
14844
|
+
request = parsed;
|
|
14845
|
+
} catch {
|
|
14846
|
+
warnings.push({
|
|
14847
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
|
|
14848
|
+
severity: "error",
|
|
14849
|
+
message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
|
|
14850
|
+
hint: "The request value must be a base64url-encoded JCS JSON object.",
|
|
14851
|
+
path: `${idx}.request`
|
|
14852
|
+
});
|
|
14853
|
+
continue;
|
|
14854
|
+
}
|
|
14855
|
+
if (!request["currency"]) {
|
|
14856
|
+
warnings.push({
|
|
14857
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
|
|
14858
|
+
severity: "error",
|
|
14859
|
+
message: `Payment challenge ${i} is missing currency in the request object.`,
|
|
14860
|
+
hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
|
|
14861
|
+
path: `${idx}.request.currency`
|
|
14862
|
+
});
|
|
14863
|
+
}
|
|
14864
|
+
const amount = request["amount"];
|
|
14865
|
+
if (amount === void 0 || amount === null) {
|
|
14866
|
+
warnings.push({
|
|
14867
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
14868
|
+
severity: "error",
|
|
14869
|
+
message: `Payment challenge ${i} is missing amount in the request object.`,
|
|
14870
|
+
hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
|
|
14871
|
+
path: `${idx}.request.amount`
|
|
14872
|
+
});
|
|
14873
|
+
} else if (typeof amount !== "string" && typeof amount !== "number") {
|
|
14874
|
+
warnings.push({
|
|
14875
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
14876
|
+
severity: "error",
|
|
14877
|
+
message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
|
|
14878
|
+
hint: "Set amount to a raw token-unit string.",
|
|
14879
|
+
path: `${idx}.request.amount`
|
|
14880
|
+
});
|
|
14881
|
+
}
|
|
14882
|
+
if (!request["recipient"]) {
|
|
14883
|
+
warnings.push({
|
|
14884
|
+
code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
|
|
14885
|
+
severity: "error",
|
|
14886
|
+
message: `Payment challenge ${i} is missing recipient in the request object.`,
|
|
14887
|
+
hint: "Set recipient to the wallet address that should receive payment.",
|
|
14888
|
+
path: `${idx}.request.recipient`
|
|
14889
|
+
});
|
|
14890
|
+
}
|
|
14891
|
+
}
|
|
14892
|
+
return warnings;
|
|
14893
|
+
}
|
|
14894
|
+
|
|
14735
14895
|
// src/audit/warnings/l3.ts
|
|
14736
14896
|
function getWarningsFor402Body(body) {
|
|
14737
14897
|
if (!isRecord(body)) {
|
|
@@ -14861,17 +15021,28 @@ function getWarningsForL3(l3) {
|
|
|
14861
15021
|
hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
|
|
14862
15022
|
});
|
|
14863
15023
|
}
|
|
14864
|
-
if (l3.authMode === "paid" && !l3.protocols?.length) {
|
|
15024
|
+
if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
|
|
14865
15025
|
warnings.push({
|
|
14866
15026
|
code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
|
|
14867
15027
|
severity: "info",
|
|
14868
15028
|
message: "Paid endpoint does not declare supported payment protocols.",
|
|
14869
|
-
hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
|
|
15029
|
+
hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
|
|
15030
|
+
});
|
|
15031
|
+
}
|
|
15032
|
+
if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
|
|
15033
|
+
warnings.push({
|
|
15034
|
+
code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
|
|
15035
|
+
severity: "warn",
|
|
15036
|
+
message: "Paid endpoint did not return payment options in the 402 response.",
|
|
15037
|
+
hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
|
|
14870
15038
|
});
|
|
14871
15039
|
}
|
|
14872
15040
|
if (l3.paymentRequiredBody !== void 0) {
|
|
14873
15041
|
warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
|
|
14874
15042
|
}
|
|
15043
|
+
if (l3.wwwAuthenticate !== void 0) {
|
|
15044
|
+
warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
|
|
15045
|
+
}
|
|
14875
15046
|
return warnings;
|
|
14876
15047
|
}
|
|
14877
15048
|
|
|
@@ -14974,7 +15145,7 @@ function detectProtocols(response) {
|
|
|
14974
15145
|
}
|
|
14975
15146
|
const authHeader = response.headers.get("www-authenticate")?.toLowerCase() ?? "";
|
|
14976
15147
|
if (authHeader.includes("x402")) protocols.add("x402");
|
|
14977
|
-
if (
|
|
15148
|
+
if (authHeader.includes("mpp")) protocols.add("mpp");
|
|
14978
15149
|
return [...protocols];
|
|
14979
15150
|
}
|
|
14980
15151
|
function buildProbeUrl(url2, method, inputBody) {
|
|
@@ -15044,7 +15215,7 @@ function getProbe(url2, headers, signal, inputBody) {
|
|
|
15044
15215
|
|
|
15045
15216
|
// src/core/protocols/mpp/index.ts
|
|
15046
15217
|
var TEMPO_DEFAULT_CHAIN_ID = 4217;
|
|
15047
|
-
function
|
|
15218
|
+
function parseAuthParams2(segment) {
|
|
15048
15219
|
const params = {};
|
|
15049
15220
|
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
15050
15221
|
let match;
|
|
@@ -15054,11 +15225,11 @@ function parseAuthParams(segment) {
|
|
|
15054
15225
|
return params;
|
|
15055
15226
|
}
|
|
15056
15227
|
function extractPaymentOptions4(wwwAuthenticate) {
|
|
15057
|
-
if (!
|
|
15228
|
+
if (!wwwAuthenticate) return [];
|
|
15058
15229
|
const options = [];
|
|
15059
15230
|
for (const segment of wwwAuthenticate.split(/,\s*(?=Payment\s)/i)) {
|
|
15060
15231
|
const stripped = segment.replace(/^Payment\s+/i, "").trim();
|
|
15061
|
-
const params =
|
|
15232
|
+
const params = parseAuthParams2(stripped);
|
|
15062
15233
|
const paymentMethod = params["method"];
|
|
15063
15234
|
const intent = params["intent"];
|
|
15064
15235
|
const realm = params["realm"];
|
|
@@ -15083,12 +15254,10 @@ function extractPaymentOptions4(wwwAuthenticate) {
|
|
|
15083
15254
|
if (!asset || !amount) continue;
|
|
15084
15255
|
options.push({
|
|
15085
15256
|
protocol: "mpp",
|
|
15086
|
-
// isMmmEnabled
|
|
15087
15257
|
paymentMethod,
|
|
15088
15258
|
intent,
|
|
15089
15259
|
realm,
|
|
15090
15260
|
network: `tempo:${String(chainId)}`,
|
|
15091
|
-
// isMmmEnabled
|
|
15092
15261
|
asset,
|
|
15093
15262
|
amount,
|
|
15094
15263
|
...decimals != null ? { decimals } : {},
|
|
@@ -15216,7 +15385,7 @@ function parseOperationProtocols(operation) {
|
|
|
15216
15385
|
const paymentInfo = operation["x-payment-info"];
|
|
15217
15386
|
if (!isRecord(paymentInfo) || !Array.isArray(paymentInfo.protocols)) return void 0;
|
|
15218
15387
|
const protocols = paymentInfo.protocols.filter(
|
|
15219
|
-
(protocol) => typeof protocol === "string" && protocol.length > 0
|
|
15388
|
+
(protocol) => typeof protocol === "string" && protocol.length > 0
|
|
15220
15389
|
);
|
|
15221
15390
|
return protocols.length > 0 ? protocols : void 0;
|
|
15222
15391
|
}
|
|
@@ -15247,8 +15416,7 @@ function getL3ForProbe(probe, path, method) {
|
|
|
15247
15416
|
const outputSchema = probeResult.paymentRequiredBody ? parseOutputSchema(probeResult.paymentRequiredBody) : void 0;
|
|
15248
15417
|
const paymentOptions = [
|
|
15249
15418
|
...probeResult.paymentRequiredBody ? extractPaymentOptions3(probeResult.paymentRequiredBody) : [],
|
|
15250
|
-
...
|
|
15251
|
-
// isMmmEnabled
|
|
15419
|
+
...extractPaymentOptions4(probeResult.wwwAuthenticate)
|
|
15252
15420
|
];
|
|
15253
15421
|
return {
|
|
15254
15422
|
source: "probe",
|
|
@@ -15257,7 +15425,8 @@ function getL3ForProbe(probe, path, method) {
|
|
|
15257
15425
|
...inputSchema ? { inputSchema } : {},
|
|
15258
15426
|
...outputSchema ? { outputSchema } : {},
|
|
15259
15427
|
...paymentOptions.length ? { paymentOptions } : {},
|
|
15260
|
-
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
|
|
15428
|
+
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
|
|
15429
|
+
...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
|
|
15261
15430
|
};
|
|
15262
15431
|
}
|
|
15263
15432
|
async function attachProbePayload(url2, advisories) {
|
|
@@ -15288,7 +15457,7 @@ async function checkEndpointSchema(options) {
|
|
|
15288
15457
|
const endpoint = new URL(ensureProtocol(options.url));
|
|
15289
15458
|
const origin = normalizeOrigin(endpoint.origin);
|
|
15290
15459
|
const path = normalizePath(endpoint.pathname || "/");
|
|
15291
|
-
if (options.sampleInputBody !== void 0) {
|
|
15460
|
+
if (options.probe || options.sampleInputBody !== void 0) {
|
|
15292
15461
|
const probeResult2 = await getProbe(
|
|
15293
15462
|
endpoint.href,
|
|
15294
15463
|
options.headers,
|
package/dist/cli.js
CHANGED
|
@@ -13834,7 +13834,6 @@ var WellKnownDocSchema = external_exports.object({
|
|
|
13834
13834
|
version: external_exports.number().optional(),
|
|
13835
13835
|
resources: external_exports.array(external_exports.string()).default([]),
|
|
13836
13836
|
mppResources: external_exports.array(external_exports.string()).optional(),
|
|
13837
|
-
// isMmmEnabled
|
|
13838
13837
|
description: external_exports.string().optional(),
|
|
13839
13838
|
ownershipProofs: external_exports.array(external_exports.string()).optional(),
|
|
13840
13839
|
instructions: external_exports.string().optional()
|
|
@@ -13944,9 +13943,6 @@ function fetchSafe(url2, init) {
|
|
|
13944
13943
|
return ResultAsync.fromPromise(fetch(url2, init), toFetchError);
|
|
13945
13944
|
}
|
|
13946
13945
|
|
|
13947
|
-
// src/mmm-enabled.ts
|
|
13948
|
-
var isMmmEnabled = () => "1.1.2".includes("-mmm");
|
|
13949
|
-
|
|
13950
13946
|
// src/core/source/openapi/index.ts
|
|
13951
13947
|
var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
13952
13948
|
const routes = [];
|
|
@@ -13956,9 +13952,7 @@ var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
|
|
|
13956
13952
|
if (!operation) continue;
|
|
13957
13953
|
const authMode = inferAuthMode(operation, doc.security, doc.components?.securitySchemes) ?? void 0;
|
|
13958
13954
|
const p = operation["x-payment-info"];
|
|
13959
|
-
const protocols = (p?.protocols ?? []).filter(
|
|
13960
|
-
(proto) => proto !== "mpp" || isMmmEnabled()
|
|
13961
|
-
);
|
|
13955
|
+
const protocols = (p?.protocols ?? []).filter((proto) => proto.length > 0);
|
|
13962
13956
|
const pricing = (authMode === "paid" || authMode === "apiKey+paid") && p ? {
|
|
13963
13957
|
pricingMode: p.pricingMode,
|
|
13964
13958
|
...p.price ? { price: p.price } : {},
|
|
@@ -14072,6 +14066,7 @@ function checkL2ForOpenAPI(openApi) {
|
|
|
14072
14066
|
return {
|
|
14073
14067
|
...openApi.info.title ? { title: openApi.info.title } : {},
|
|
14074
14068
|
...openApi.info.description ? { description: openApi.info.description } : {},
|
|
14069
|
+
...openApi.info.version ? { version: openApi.info.version } : {},
|
|
14075
14070
|
routes,
|
|
14076
14071
|
source: "openapi"
|
|
14077
14072
|
};
|
|
@@ -14121,9 +14116,23 @@ var AUDIT_CODES = {
|
|
|
14121
14116
|
L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
|
|
14122
14117
|
L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
|
|
14123
14118
|
L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
|
|
14119
|
+
L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
|
|
14124
14120
|
// ─── L4 guidance checks ──────────────────────────────────────────────────────
|
|
14125
14121
|
L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
|
|
14126
|
-
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
|
|
14122
|
+
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
|
|
14123
|
+
// ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
|
|
14124
|
+
MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
|
|
14125
|
+
MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
|
|
14126
|
+
MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
|
|
14127
|
+
MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
|
|
14128
|
+
MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
|
|
14129
|
+
MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
|
|
14130
|
+
MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
|
|
14131
|
+
MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
|
|
14132
|
+
MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
|
|
14133
|
+
MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
|
|
14134
|
+
MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
|
|
14135
|
+
MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
|
|
14127
14136
|
};
|
|
14128
14137
|
|
|
14129
14138
|
// src/audit/warnings/sources.ts
|
|
@@ -14702,6 +14711,157 @@ function validateWithCoinbaseSchema2(body) {
|
|
|
14702
14711
|
});
|
|
14703
14712
|
}
|
|
14704
14713
|
|
|
14714
|
+
// src/audit/warnings/mpp.ts
|
|
14715
|
+
function parseAuthParams(segment) {
|
|
14716
|
+
const params = {};
|
|
14717
|
+
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
14718
|
+
let match;
|
|
14719
|
+
while ((match = re.exec(segment)) !== null) {
|
|
14720
|
+
params[match[1]] = match[2] ?? match[3] ?? "";
|
|
14721
|
+
}
|
|
14722
|
+
return params;
|
|
14723
|
+
}
|
|
14724
|
+
function getWarningsForMppHeader(wwwAuthenticate) {
|
|
14725
|
+
if (!wwwAuthenticate?.trim()) {
|
|
14726
|
+
return [
|
|
14727
|
+
{
|
|
14728
|
+
code: AUDIT_CODES.MPP_HEADER_MISSING,
|
|
14729
|
+
severity: "error",
|
|
14730
|
+
message: "WWW-Authenticate header is absent.",
|
|
14731
|
+
hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
|
|
14732
|
+
}
|
|
14733
|
+
];
|
|
14734
|
+
}
|
|
14735
|
+
const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
|
|
14736
|
+
if (segments.length === 0) {
|
|
14737
|
+
return [
|
|
14738
|
+
{
|
|
14739
|
+
code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
|
|
14740
|
+
severity: "error",
|
|
14741
|
+
message: "WWW-Authenticate header contains no Payment challenges.",
|
|
14742
|
+
hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
|
|
14743
|
+
}
|
|
14744
|
+
];
|
|
14745
|
+
}
|
|
14746
|
+
const warnings = [];
|
|
14747
|
+
for (let i = 0; i < segments.length; i++) {
|
|
14748
|
+
const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
|
|
14749
|
+
const params = parseAuthParams(stripped);
|
|
14750
|
+
const idx = `WWW-Authenticate[${i}]`;
|
|
14751
|
+
if (!params["id"]) {
|
|
14752
|
+
warnings.push({
|
|
14753
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
|
|
14754
|
+
severity: "error",
|
|
14755
|
+
message: `Payment challenge ${i} is missing the id parameter.`,
|
|
14756
|
+
hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
|
|
14757
|
+
path: `${idx}.id`
|
|
14758
|
+
});
|
|
14759
|
+
}
|
|
14760
|
+
if (!params["method"]) {
|
|
14761
|
+
warnings.push({
|
|
14762
|
+
code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
|
|
14763
|
+
severity: "error",
|
|
14764
|
+
message: `Payment challenge ${i} is missing the method parameter.`,
|
|
14765
|
+
hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
|
|
14766
|
+
path: `${idx}.method`
|
|
14767
|
+
});
|
|
14768
|
+
}
|
|
14769
|
+
if (!params["intent"]) {
|
|
14770
|
+
warnings.push({
|
|
14771
|
+
code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
|
|
14772
|
+
severity: "error",
|
|
14773
|
+
message: `Payment challenge ${i} is missing the intent parameter.`,
|
|
14774
|
+
hint: 'Set intent="charge" on the Payment challenge.',
|
|
14775
|
+
path: `${idx}.intent`
|
|
14776
|
+
});
|
|
14777
|
+
}
|
|
14778
|
+
if (!params["realm"]) {
|
|
14779
|
+
warnings.push({
|
|
14780
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
|
|
14781
|
+
severity: "error",
|
|
14782
|
+
message: `Payment challenge ${i} is missing the realm parameter.`,
|
|
14783
|
+
hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
|
|
14784
|
+
path: `${idx}.realm`
|
|
14785
|
+
});
|
|
14786
|
+
}
|
|
14787
|
+
if (!params["expires"]) {
|
|
14788
|
+
warnings.push({
|
|
14789
|
+
code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
|
|
14790
|
+
severity: "error",
|
|
14791
|
+
message: `Payment challenge ${i} is missing the expires parameter.`,
|
|
14792
|
+
hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
|
|
14793
|
+
path: `${idx}.expires`
|
|
14794
|
+
});
|
|
14795
|
+
}
|
|
14796
|
+
const requestStr = params["request"];
|
|
14797
|
+
if (!requestStr) {
|
|
14798
|
+
warnings.push({
|
|
14799
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
|
|
14800
|
+
severity: "error",
|
|
14801
|
+
message: `Payment challenge ${i} is missing the request field.`,
|
|
14802
|
+
hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
|
|
14803
|
+
path: `${idx}.request`
|
|
14804
|
+
});
|
|
14805
|
+
continue;
|
|
14806
|
+
}
|
|
14807
|
+
let request;
|
|
14808
|
+
try {
|
|
14809
|
+
const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
|
|
14810
|
+
const parsed = JSON.parse(decoded);
|
|
14811
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
14812
|
+
throw new Error("not an object");
|
|
14813
|
+
}
|
|
14814
|
+
request = parsed;
|
|
14815
|
+
} catch {
|
|
14816
|
+
warnings.push({
|
|
14817
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
|
|
14818
|
+
severity: "error",
|
|
14819
|
+
message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
|
|
14820
|
+
hint: "The request value must be a base64url-encoded JCS JSON object.",
|
|
14821
|
+
path: `${idx}.request`
|
|
14822
|
+
});
|
|
14823
|
+
continue;
|
|
14824
|
+
}
|
|
14825
|
+
if (!request["currency"]) {
|
|
14826
|
+
warnings.push({
|
|
14827
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
|
|
14828
|
+
severity: "error",
|
|
14829
|
+
message: `Payment challenge ${i} is missing currency in the request object.`,
|
|
14830
|
+
hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
|
|
14831
|
+
path: `${idx}.request.currency`
|
|
14832
|
+
});
|
|
14833
|
+
}
|
|
14834
|
+
const amount = request["amount"];
|
|
14835
|
+
if (amount === void 0 || amount === null) {
|
|
14836
|
+
warnings.push({
|
|
14837
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
14838
|
+
severity: "error",
|
|
14839
|
+
message: `Payment challenge ${i} is missing amount in the request object.`,
|
|
14840
|
+
hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
|
|
14841
|
+
path: `${idx}.request.amount`
|
|
14842
|
+
});
|
|
14843
|
+
} else if (typeof amount !== "string" && typeof amount !== "number") {
|
|
14844
|
+
warnings.push({
|
|
14845
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
14846
|
+
severity: "error",
|
|
14847
|
+
message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
|
|
14848
|
+
hint: "Set amount to a raw token-unit string.",
|
|
14849
|
+
path: `${idx}.request.amount`
|
|
14850
|
+
});
|
|
14851
|
+
}
|
|
14852
|
+
if (!request["recipient"]) {
|
|
14853
|
+
warnings.push({
|
|
14854
|
+
code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
|
|
14855
|
+
severity: "error",
|
|
14856
|
+
message: `Payment challenge ${i} is missing recipient in the request object.`,
|
|
14857
|
+
hint: "Set recipient to the wallet address that should receive payment.",
|
|
14858
|
+
path: `${idx}.request.recipient`
|
|
14859
|
+
});
|
|
14860
|
+
}
|
|
14861
|
+
}
|
|
14862
|
+
return warnings;
|
|
14863
|
+
}
|
|
14864
|
+
|
|
14705
14865
|
// src/audit/warnings/l3.ts
|
|
14706
14866
|
function getWarningsFor402Body(body) {
|
|
14707
14867
|
if (!isRecord(body)) {
|
|
@@ -14831,17 +14991,28 @@ function getWarningsForL3(l3) {
|
|
|
14831
14991
|
hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
|
|
14832
14992
|
});
|
|
14833
14993
|
}
|
|
14834
|
-
if (l3.authMode === "paid" && !l3.protocols?.length) {
|
|
14994
|
+
if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
|
|
14835
14995
|
warnings.push({
|
|
14836
14996
|
code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
|
|
14837
14997
|
severity: "info",
|
|
14838
14998
|
message: "Paid endpoint does not declare supported payment protocols.",
|
|
14839
|
-
hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
|
|
14999
|
+
hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
|
|
15000
|
+
});
|
|
15001
|
+
}
|
|
15002
|
+
if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
|
|
15003
|
+
warnings.push({
|
|
15004
|
+
code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
|
|
15005
|
+
severity: "warn",
|
|
15006
|
+
message: "Paid endpoint did not return payment options in the 402 response.",
|
|
15007
|
+
hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
|
|
14840
15008
|
});
|
|
14841
15009
|
}
|
|
14842
15010
|
if (l3.paymentRequiredBody !== void 0) {
|
|
14843
15011
|
warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
|
|
14844
15012
|
}
|
|
15013
|
+
if (l3.wwwAuthenticate !== void 0) {
|
|
15014
|
+
warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
|
|
15015
|
+
}
|
|
14845
15016
|
return warnings;
|
|
14846
15017
|
}
|
|
14847
15018
|
|
|
@@ -14944,7 +15115,7 @@ function detectProtocols(response) {
|
|
|
14944
15115
|
}
|
|
14945
15116
|
const authHeader = response.headers.get("www-authenticate")?.toLowerCase() ?? "";
|
|
14946
15117
|
if (authHeader.includes("x402")) protocols.add("x402");
|
|
14947
|
-
if (
|
|
15118
|
+
if (authHeader.includes("mpp")) protocols.add("mpp");
|
|
14948
15119
|
return [...protocols];
|
|
14949
15120
|
}
|
|
14950
15121
|
function buildProbeUrl(url2, method, inputBody) {
|
|
@@ -15014,7 +15185,7 @@ function getProbe(url2, headers, signal, inputBody) {
|
|
|
15014
15185
|
|
|
15015
15186
|
// src/core/protocols/mpp/index.ts
|
|
15016
15187
|
var TEMPO_DEFAULT_CHAIN_ID = 4217;
|
|
15017
|
-
function
|
|
15188
|
+
function parseAuthParams2(segment) {
|
|
15018
15189
|
const params = {};
|
|
15019
15190
|
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
15020
15191
|
let match;
|
|
@@ -15024,11 +15195,11 @@ function parseAuthParams(segment) {
|
|
|
15024
15195
|
return params;
|
|
15025
15196
|
}
|
|
15026
15197
|
function extractPaymentOptions4(wwwAuthenticate) {
|
|
15027
|
-
if (!
|
|
15198
|
+
if (!wwwAuthenticate) return [];
|
|
15028
15199
|
const options = [];
|
|
15029
15200
|
for (const segment of wwwAuthenticate.split(/,\s*(?=Payment\s)/i)) {
|
|
15030
15201
|
const stripped = segment.replace(/^Payment\s+/i, "").trim();
|
|
15031
|
-
const params =
|
|
15202
|
+
const params = parseAuthParams2(stripped);
|
|
15032
15203
|
const paymentMethod = params["method"];
|
|
15033
15204
|
const intent = params["intent"];
|
|
15034
15205
|
const realm = params["realm"];
|
|
@@ -15053,12 +15224,10 @@ function extractPaymentOptions4(wwwAuthenticate) {
|
|
|
15053
15224
|
if (!asset || !amount) continue;
|
|
15054
15225
|
options.push({
|
|
15055
15226
|
protocol: "mpp",
|
|
15056
|
-
// isMmmEnabled
|
|
15057
15227
|
paymentMethod,
|
|
15058
15228
|
intent,
|
|
15059
15229
|
realm,
|
|
15060
15230
|
network: `tempo:${String(chainId)}`,
|
|
15061
|
-
// isMmmEnabled
|
|
15062
15231
|
asset,
|
|
15063
15232
|
amount,
|
|
15064
15233
|
...decimals != null ? { decimals } : {},
|
|
@@ -15186,7 +15355,7 @@ function parseOperationProtocols(operation) {
|
|
|
15186
15355
|
const paymentInfo = operation["x-payment-info"];
|
|
15187
15356
|
if (!isRecord(paymentInfo) || !Array.isArray(paymentInfo.protocols)) return void 0;
|
|
15188
15357
|
const protocols = paymentInfo.protocols.filter(
|
|
15189
|
-
(protocol) => typeof protocol === "string" && protocol.length > 0
|
|
15358
|
+
(protocol) => typeof protocol === "string" && protocol.length > 0
|
|
15190
15359
|
);
|
|
15191
15360
|
return protocols.length > 0 ? protocols : void 0;
|
|
15192
15361
|
}
|
|
@@ -15217,8 +15386,7 @@ function getL3ForProbe(probe, path, method) {
|
|
|
15217
15386
|
const outputSchema = probeResult.paymentRequiredBody ? parseOutputSchema(probeResult.paymentRequiredBody) : void 0;
|
|
15218
15387
|
const paymentOptions = [
|
|
15219
15388
|
...probeResult.paymentRequiredBody ? extractPaymentOptions3(probeResult.paymentRequiredBody) : [],
|
|
15220
|
-
...
|
|
15221
|
-
// isMmmEnabled
|
|
15389
|
+
...extractPaymentOptions4(probeResult.wwwAuthenticate)
|
|
15222
15390
|
];
|
|
15223
15391
|
return {
|
|
15224
15392
|
source: "probe",
|
|
@@ -15227,7 +15395,8 @@ function getL3ForProbe(probe, path, method) {
|
|
|
15227
15395
|
...inputSchema ? { inputSchema } : {},
|
|
15228
15396
|
...outputSchema ? { outputSchema } : {},
|
|
15229
15397
|
...paymentOptions.length ? { paymentOptions } : {},
|
|
15230
|
-
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
|
|
15398
|
+
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
|
|
15399
|
+
...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
|
|
15231
15400
|
};
|
|
15232
15401
|
}
|
|
15233
15402
|
async function attachProbePayload(url2, advisories) {
|
|
@@ -15258,7 +15427,7 @@ async function checkEndpointSchema(options) {
|
|
|
15258
15427
|
const endpoint = new URL(ensureProtocol(options.url));
|
|
15259
15428
|
const origin = normalizeOrigin(endpoint.origin);
|
|
15260
15429
|
const path = normalizePath(endpoint.pathname || "/");
|
|
15261
|
-
if (options.sampleInputBody !== void 0) {
|
|
15430
|
+
if (options.probe || options.sampleInputBody !== void 0) {
|
|
15262
15431
|
const probeResult2 = await getProbe(
|
|
15263
15432
|
endpoint.href,
|
|
15264
15433
|
options.headers,
|