@agentcash/discovery 1.1.3 → 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 +183 -6
- package/dist/cli.js +183 -6
- package/dist/index.cjs +183 -4
- package/dist/index.d.cts +33 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +182 -4
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -14146,9 +14146,23 @@ var AUDIT_CODES = {
|
|
|
14146
14146
|
L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
|
|
14147
14147
|
L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
|
|
14148
14148
|
L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
|
|
14149
|
+
L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
|
|
14149
14150
|
// ─── L4 guidance checks ──────────────────────────────────────────────────────
|
|
14150
14151
|
L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
|
|
14151
|
-
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"
|
|
14152
14166
|
};
|
|
14153
14167
|
|
|
14154
14168
|
// src/audit/warnings/sources.ts
|
|
@@ -14727,6 +14741,157 @@ function validateWithCoinbaseSchema2(body) {
|
|
|
14727
14741
|
});
|
|
14728
14742
|
}
|
|
14729
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
|
+
|
|
14730
14895
|
// src/audit/warnings/l3.ts
|
|
14731
14896
|
function getWarningsFor402Body(body) {
|
|
14732
14897
|
if (!isRecord(body)) {
|
|
@@ -14856,17 +15021,28 @@ function getWarningsForL3(l3) {
|
|
|
14856
15021
|
hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
|
|
14857
15022
|
});
|
|
14858
15023
|
}
|
|
14859
|
-
if (l3.authMode === "paid" && !l3.protocols?.length) {
|
|
15024
|
+
if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
|
|
14860
15025
|
warnings.push({
|
|
14861
15026
|
code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
|
|
14862
15027
|
severity: "info",
|
|
14863
15028
|
message: "Paid endpoint does not declare supported payment protocols.",
|
|
14864
|
-
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."
|
|
14865
15038
|
});
|
|
14866
15039
|
}
|
|
14867
15040
|
if (l3.paymentRequiredBody !== void 0) {
|
|
14868
15041
|
warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
|
|
14869
15042
|
}
|
|
15043
|
+
if (l3.wwwAuthenticate !== void 0) {
|
|
15044
|
+
warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
|
|
15045
|
+
}
|
|
14870
15046
|
return warnings;
|
|
14871
15047
|
}
|
|
14872
15048
|
|
|
@@ -15039,7 +15215,7 @@ function getProbe(url2, headers, signal, inputBody) {
|
|
|
15039
15215
|
|
|
15040
15216
|
// src/core/protocols/mpp/index.ts
|
|
15041
15217
|
var TEMPO_DEFAULT_CHAIN_ID = 4217;
|
|
15042
|
-
function
|
|
15218
|
+
function parseAuthParams2(segment) {
|
|
15043
15219
|
const params = {};
|
|
15044
15220
|
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
15045
15221
|
let match;
|
|
@@ -15053,7 +15229,7 @@ function extractPaymentOptions4(wwwAuthenticate) {
|
|
|
15053
15229
|
const options = [];
|
|
15054
15230
|
for (const segment of wwwAuthenticate.split(/,\s*(?=Payment\s)/i)) {
|
|
15055
15231
|
const stripped = segment.replace(/^Payment\s+/i, "").trim();
|
|
15056
|
-
const params =
|
|
15232
|
+
const params = parseAuthParams2(stripped);
|
|
15057
15233
|
const paymentMethod = params["method"];
|
|
15058
15234
|
const intent = params["intent"];
|
|
15059
15235
|
const realm = params["realm"];
|
|
@@ -15249,7 +15425,8 @@ function getL3ForProbe(probe, path, method) {
|
|
|
15249
15425
|
...inputSchema ? { inputSchema } : {},
|
|
15250
15426
|
...outputSchema ? { outputSchema } : {},
|
|
15251
15427
|
...paymentOptions.length ? { paymentOptions } : {},
|
|
15252
|
-
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
|
|
15428
|
+
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
|
|
15429
|
+
...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
|
|
15253
15430
|
};
|
|
15254
15431
|
}
|
|
15255
15432
|
async function attachProbePayload(url2, advisories) {
|
package/dist/cli.js
CHANGED
|
@@ -14116,9 +14116,23 @@ var AUDIT_CODES = {
|
|
|
14116
14116
|
L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
|
|
14117
14117
|
L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
|
|
14118
14118
|
L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
|
|
14119
|
+
L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
|
|
14119
14120
|
// ─── L4 guidance checks ──────────────────────────────────────────────────────
|
|
14120
14121
|
L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
|
|
14121
|
-
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"
|
|
14122
14136
|
};
|
|
14123
14137
|
|
|
14124
14138
|
// src/audit/warnings/sources.ts
|
|
@@ -14697,6 +14711,157 @@ function validateWithCoinbaseSchema2(body) {
|
|
|
14697
14711
|
});
|
|
14698
14712
|
}
|
|
14699
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
|
+
|
|
14700
14865
|
// src/audit/warnings/l3.ts
|
|
14701
14866
|
function getWarningsFor402Body(body) {
|
|
14702
14867
|
if (!isRecord(body)) {
|
|
@@ -14826,17 +14991,28 @@ function getWarningsForL3(l3) {
|
|
|
14826
14991
|
hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
|
|
14827
14992
|
});
|
|
14828
14993
|
}
|
|
14829
|
-
if (l3.authMode === "paid" && !l3.protocols?.length) {
|
|
14994
|
+
if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
|
|
14830
14995
|
warnings.push({
|
|
14831
14996
|
code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
|
|
14832
14997
|
severity: "info",
|
|
14833
14998
|
message: "Paid endpoint does not declare supported payment protocols.",
|
|
14834
|
-
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."
|
|
14835
15008
|
});
|
|
14836
15009
|
}
|
|
14837
15010
|
if (l3.paymentRequiredBody !== void 0) {
|
|
14838
15011
|
warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
|
|
14839
15012
|
}
|
|
15013
|
+
if (l3.wwwAuthenticate !== void 0) {
|
|
15014
|
+
warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
|
|
15015
|
+
}
|
|
14840
15016
|
return warnings;
|
|
14841
15017
|
}
|
|
14842
15018
|
|
|
@@ -15009,7 +15185,7 @@ function getProbe(url2, headers, signal, inputBody) {
|
|
|
15009
15185
|
|
|
15010
15186
|
// src/core/protocols/mpp/index.ts
|
|
15011
15187
|
var TEMPO_DEFAULT_CHAIN_ID = 4217;
|
|
15012
|
-
function
|
|
15188
|
+
function parseAuthParams2(segment) {
|
|
15013
15189
|
const params = {};
|
|
15014
15190
|
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
15015
15191
|
let match;
|
|
@@ -15023,7 +15199,7 @@ function extractPaymentOptions4(wwwAuthenticate) {
|
|
|
15023
15199
|
const options = [];
|
|
15024
15200
|
for (const segment of wwwAuthenticate.split(/,\s*(?=Payment\s)/i)) {
|
|
15025
15201
|
const stripped = segment.replace(/^Payment\s+/i, "").trim();
|
|
15026
|
-
const params =
|
|
15202
|
+
const params = parseAuthParams2(stripped);
|
|
15027
15203
|
const paymentMethod = params["method"];
|
|
15028
15204
|
const intent = params["intent"];
|
|
15029
15205
|
const realm = params["realm"];
|
|
@@ -15219,7 +15395,8 @@ function getL3ForProbe(probe, path, method) {
|
|
|
15219
15395
|
...inputSchema ? { inputSchema } : {},
|
|
15220
15396
|
...outputSchema ? { outputSchema } : {},
|
|
15221
15397
|
...paymentOptions.length ? { paymentOptions } : {},
|
|
15222
|
-
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
|
|
15398
|
+
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
|
|
15399
|
+
...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
|
|
15223
15400
|
};
|
|
15224
15401
|
}
|
|
15225
15402
|
async function attachProbePayload(url2, advisories) {
|
package/dist/index.cjs
CHANGED
|
@@ -50,6 +50,7 @@ __export(index_exports, {
|
|
|
50
50
|
getWarningsForL2: () => getWarningsForL2,
|
|
51
51
|
getWarningsForL3: () => getWarningsForL3,
|
|
52
52
|
getWarningsForL4: () => getWarningsForL4,
|
|
53
|
+
getWarningsForMppHeader: () => getWarningsForMppHeader,
|
|
53
54
|
getWarningsForOpenAPI: () => getWarningsForOpenAPI,
|
|
54
55
|
getWarningsForWellKnown: () => getWarningsForWellKnown,
|
|
55
56
|
getWellKnown: () => getWellKnown,
|
|
@@ -14930,7 +14931,8 @@ function getL3ForProbe(probe, path, method) {
|
|
|
14930
14931
|
...inputSchema ? { inputSchema } : {},
|
|
14931
14932
|
...outputSchema ? { outputSchema } : {},
|
|
14932
14933
|
...paymentOptions.length ? { paymentOptions } : {},
|
|
14933
|
-
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
|
|
14934
|
+
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
|
|
14935
|
+
...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
|
|
14934
14936
|
};
|
|
14935
14937
|
}
|
|
14936
14938
|
async function attachProbePayload(url2, advisories) {
|
|
@@ -15111,9 +15113,23 @@ var AUDIT_CODES = {
|
|
|
15111
15113
|
L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
|
|
15112
15114
|
L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
|
|
15113
15115
|
L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
|
|
15116
|
+
L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
|
|
15114
15117
|
// ─── L4 guidance checks ──────────────────────────────────────────────────────
|
|
15115
15118
|
L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
|
|
15116
|
-
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
|
|
15119
|
+
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
|
|
15120
|
+
// ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
|
|
15121
|
+
MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
|
|
15122
|
+
MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
|
|
15123
|
+
MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
|
|
15124
|
+
MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
|
|
15125
|
+
MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
|
|
15126
|
+
MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
|
|
15127
|
+
MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
|
|
15128
|
+
MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
|
|
15129
|
+
MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
|
|
15130
|
+
MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
|
|
15131
|
+
MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
|
|
15132
|
+
MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
|
|
15117
15133
|
};
|
|
15118
15134
|
|
|
15119
15135
|
// src/core/protocols/x402/v1/coinbase-schema.ts
|
|
@@ -15202,6 +15218,157 @@ function validateWithCoinbaseSchema2(body) {
|
|
|
15202
15218
|
});
|
|
15203
15219
|
}
|
|
15204
15220
|
|
|
15221
|
+
// src/audit/warnings/mpp.ts
|
|
15222
|
+
function parseAuthParams2(segment) {
|
|
15223
|
+
const params = {};
|
|
15224
|
+
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
15225
|
+
let match;
|
|
15226
|
+
while ((match = re.exec(segment)) !== null) {
|
|
15227
|
+
params[match[1]] = match[2] ?? match[3] ?? "";
|
|
15228
|
+
}
|
|
15229
|
+
return params;
|
|
15230
|
+
}
|
|
15231
|
+
function getWarningsForMppHeader(wwwAuthenticate) {
|
|
15232
|
+
if (!wwwAuthenticate?.trim()) {
|
|
15233
|
+
return [
|
|
15234
|
+
{
|
|
15235
|
+
code: AUDIT_CODES.MPP_HEADER_MISSING,
|
|
15236
|
+
severity: "error",
|
|
15237
|
+
message: "WWW-Authenticate header is absent.",
|
|
15238
|
+
hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
|
|
15239
|
+
}
|
|
15240
|
+
];
|
|
15241
|
+
}
|
|
15242
|
+
const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
|
|
15243
|
+
if (segments.length === 0) {
|
|
15244
|
+
return [
|
|
15245
|
+
{
|
|
15246
|
+
code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
|
|
15247
|
+
severity: "error",
|
|
15248
|
+
message: "WWW-Authenticate header contains no Payment challenges.",
|
|
15249
|
+
hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
|
|
15250
|
+
}
|
|
15251
|
+
];
|
|
15252
|
+
}
|
|
15253
|
+
const warnings = [];
|
|
15254
|
+
for (let i = 0; i < segments.length; i++) {
|
|
15255
|
+
const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
|
|
15256
|
+
const params = parseAuthParams2(stripped);
|
|
15257
|
+
const idx = `WWW-Authenticate[${i}]`;
|
|
15258
|
+
if (!params["id"]) {
|
|
15259
|
+
warnings.push({
|
|
15260
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
|
|
15261
|
+
severity: "error",
|
|
15262
|
+
message: `Payment challenge ${i} is missing the id parameter.`,
|
|
15263
|
+
hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
|
|
15264
|
+
path: `${idx}.id`
|
|
15265
|
+
});
|
|
15266
|
+
}
|
|
15267
|
+
if (!params["method"]) {
|
|
15268
|
+
warnings.push({
|
|
15269
|
+
code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
|
|
15270
|
+
severity: "error",
|
|
15271
|
+
message: `Payment challenge ${i} is missing the method parameter.`,
|
|
15272
|
+
hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
|
|
15273
|
+
path: `${idx}.method`
|
|
15274
|
+
});
|
|
15275
|
+
}
|
|
15276
|
+
if (!params["intent"]) {
|
|
15277
|
+
warnings.push({
|
|
15278
|
+
code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
|
|
15279
|
+
severity: "error",
|
|
15280
|
+
message: `Payment challenge ${i} is missing the intent parameter.`,
|
|
15281
|
+
hint: 'Set intent="charge" on the Payment challenge.',
|
|
15282
|
+
path: `${idx}.intent`
|
|
15283
|
+
});
|
|
15284
|
+
}
|
|
15285
|
+
if (!params["realm"]) {
|
|
15286
|
+
warnings.push({
|
|
15287
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
|
|
15288
|
+
severity: "error",
|
|
15289
|
+
message: `Payment challenge ${i} is missing the realm parameter.`,
|
|
15290
|
+
hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
|
|
15291
|
+
path: `${idx}.realm`
|
|
15292
|
+
});
|
|
15293
|
+
}
|
|
15294
|
+
if (!params["expires"]) {
|
|
15295
|
+
warnings.push({
|
|
15296
|
+
code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
|
|
15297
|
+
severity: "error",
|
|
15298
|
+
message: `Payment challenge ${i} is missing the expires parameter.`,
|
|
15299
|
+
hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
|
|
15300
|
+
path: `${idx}.expires`
|
|
15301
|
+
});
|
|
15302
|
+
}
|
|
15303
|
+
const requestStr = params["request"];
|
|
15304
|
+
if (!requestStr) {
|
|
15305
|
+
warnings.push({
|
|
15306
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
|
|
15307
|
+
severity: "error",
|
|
15308
|
+
message: `Payment challenge ${i} is missing the request field.`,
|
|
15309
|
+
hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
|
|
15310
|
+
path: `${idx}.request`
|
|
15311
|
+
});
|
|
15312
|
+
continue;
|
|
15313
|
+
}
|
|
15314
|
+
let request;
|
|
15315
|
+
try {
|
|
15316
|
+
const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
|
|
15317
|
+
const parsed = JSON.parse(decoded);
|
|
15318
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
15319
|
+
throw new Error("not an object");
|
|
15320
|
+
}
|
|
15321
|
+
request = parsed;
|
|
15322
|
+
} catch {
|
|
15323
|
+
warnings.push({
|
|
15324
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
|
|
15325
|
+
severity: "error",
|
|
15326
|
+
message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
|
|
15327
|
+
hint: "The request value must be a base64url-encoded JCS JSON object.",
|
|
15328
|
+
path: `${idx}.request`
|
|
15329
|
+
});
|
|
15330
|
+
continue;
|
|
15331
|
+
}
|
|
15332
|
+
if (!request["currency"]) {
|
|
15333
|
+
warnings.push({
|
|
15334
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
|
|
15335
|
+
severity: "error",
|
|
15336
|
+
message: `Payment challenge ${i} is missing currency in the request object.`,
|
|
15337
|
+
hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
|
|
15338
|
+
path: `${idx}.request.currency`
|
|
15339
|
+
});
|
|
15340
|
+
}
|
|
15341
|
+
const amount = request["amount"];
|
|
15342
|
+
if (amount === void 0 || amount === null) {
|
|
15343
|
+
warnings.push({
|
|
15344
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
15345
|
+
severity: "error",
|
|
15346
|
+
message: `Payment challenge ${i} is missing amount in the request object.`,
|
|
15347
|
+
hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
|
|
15348
|
+
path: `${idx}.request.amount`
|
|
15349
|
+
});
|
|
15350
|
+
} else if (typeof amount !== "string" && typeof amount !== "number") {
|
|
15351
|
+
warnings.push({
|
|
15352
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
15353
|
+
severity: "error",
|
|
15354
|
+
message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
|
|
15355
|
+
hint: "Set amount to a raw token-unit string.",
|
|
15356
|
+
path: `${idx}.request.amount`
|
|
15357
|
+
});
|
|
15358
|
+
}
|
|
15359
|
+
if (!request["recipient"]) {
|
|
15360
|
+
warnings.push({
|
|
15361
|
+
code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
|
|
15362
|
+
severity: "error",
|
|
15363
|
+
message: `Payment challenge ${i} is missing recipient in the request object.`,
|
|
15364
|
+
hint: "Set recipient to the wallet address that should receive payment.",
|
|
15365
|
+
path: `${idx}.request.recipient`
|
|
15366
|
+
});
|
|
15367
|
+
}
|
|
15368
|
+
}
|
|
15369
|
+
return warnings;
|
|
15370
|
+
}
|
|
15371
|
+
|
|
15205
15372
|
// src/audit/warnings/l3.ts
|
|
15206
15373
|
function getWarningsFor402Body(body) {
|
|
15207
15374
|
if (!isRecord(body)) {
|
|
@@ -15331,17 +15498,28 @@ function getWarningsForL3(l3) {
|
|
|
15331
15498
|
hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
|
|
15332
15499
|
});
|
|
15333
15500
|
}
|
|
15334
|
-
if (l3.authMode === "paid" && !l3.protocols?.length) {
|
|
15501
|
+
if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
|
|
15335
15502
|
warnings.push({
|
|
15336
15503
|
code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
|
|
15337
15504
|
severity: "info",
|
|
15338
15505
|
message: "Paid endpoint does not declare supported payment protocols.",
|
|
15339
|
-
hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
|
|
15506
|
+
hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
|
|
15507
|
+
});
|
|
15508
|
+
}
|
|
15509
|
+
if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
|
|
15510
|
+
warnings.push({
|
|
15511
|
+
code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
|
|
15512
|
+
severity: "warn",
|
|
15513
|
+
message: "Paid endpoint did not return payment options in the 402 response.",
|
|
15514
|
+
hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
|
|
15340
15515
|
});
|
|
15341
15516
|
}
|
|
15342
15517
|
if (l3.paymentRequiredBody !== void 0) {
|
|
15343
15518
|
warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
|
|
15344
15519
|
}
|
|
15520
|
+
if (l3.wwwAuthenticate !== void 0) {
|
|
15521
|
+
warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
|
|
15522
|
+
}
|
|
15345
15523
|
return warnings;
|
|
15346
15524
|
}
|
|
15347
15525
|
|
|
@@ -15566,6 +15744,7 @@ function getWarningsForL4(l4) {
|
|
|
15566
15744
|
getWarningsForL2,
|
|
15567
15745
|
getWarningsForL3,
|
|
15568
15746
|
getWarningsForL4,
|
|
15747
|
+
getWarningsForMppHeader,
|
|
15569
15748
|
getWarningsForOpenAPI,
|
|
15570
15749
|
getWarningsForWellKnown,
|
|
15571
15750
|
getWellKnown,
|
package/dist/index.d.cts
CHANGED
|
@@ -137,6 +137,11 @@ interface L3Result {
|
|
|
137
137
|
* and returned a 402. Used by getWarningsForL3 to run full payment-required validation.
|
|
138
138
|
*/
|
|
139
139
|
paymentRequiredBody?: unknown;
|
|
140
|
+
/**
|
|
141
|
+
* Raw WWW-Authenticate header value from the 402 response. Present when the endpoint
|
|
142
|
+
* was probed and returned a 402 with an MPP challenge. Used by getWarningsForMppHeader.
|
|
143
|
+
*/
|
|
144
|
+
wwwAuthenticate?: string;
|
|
140
145
|
}
|
|
141
146
|
interface L4Result {
|
|
142
147
|
guidance: string;
|
|
@@ -361,8 +366,21 @@ declare const AUDIT_CODES: {
|
|
|
361
366
|
readonly L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING";
|
|
362
367
|
readonly L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING";
|
|
363
368
|
readonly L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID";
|
|
369
|
+
readonly L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID";
|
|
364
370
|
readonly L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING";
|
|
365
371
|
readonly L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG";
|
|
372
|
+
readonly MPP_HEADER_MISSING: "MPP_HEADER_MISSING";
|
|
373
|
+
readonly MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES";
|
|
374
|
+
readonly MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING";
|
|
375
|
+
readonly MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING";
|
|
376
|
+
readonly MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING";
|
|
377
|
+
readonly MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING";
|
|
378
|
+
readonly MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING";
|
|
379
|
+
readonly MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING";
|
|
380
|
+
readonly MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID";
|
|
381
|
+
readonly MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING";
|
|
382
|
+
readonly MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING";
|
|
383
|
+
readonly MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING";
|
|
366
384
|
};
|
|
367
385
|
type AuditCode = (typeof AUDIT_CODES)[keyof typeof AUDIT_CODES];
|
|
368
386
|
|
|
@@ -392,4 +410,18 @@ declare function getWarningsForL3(l3: L3Result | null): AuditWarning[];
|
|
|
392
410
|
|
|
393
411
|
declare function getWarningsForL4(l4: L4Result | null): AuditWarning[];
|
|
394
412
|
|
|
395
|
-
|
|
413
|
+
/**
|
|
414
|
+
* Validates a raw WWW-Authenticate header value from an MPP 402 response and
|
|
415
|
+
* returns issues as AuditWarnings.
|
|
416
|
+
*
|
|
417
|
+
* Checks for:
|
|
418
|
+
* - Header presence
|
|
419
|
+
* - At least one Payment challenge
|
|
420
|
+
* - Required challenge parameters: id, method, intent, realm, expires, request
|
|
421
|
+
* - Valid base64url-encoded JSON in the request field
|
|
422
|
+
* - Required request fields: currency (asset), amount
|
|
423
|
+
* - Recommended request field: recipient (payTo)
|
|
424
|
+
*/
|
|
425
|
+
declare function getWarningsForMppHeader(wwwAuthenticate: string | null | undefined): AuditWarning[];
|
|
426
|
+
|
|
427
|
+
export { AUDIT_CODES, type AuditCode, type AuditSeverity, type AuditWarning, type AuthMode, type CheckEndpointNotFound, type CheckEndpointOptions, type CheckEndpointResult, type CheckEndpointSuccess, type DiscoverOriginSchemaNotFound, type DiscoverOriginSchemaOptions, type DiscoverOriginSchemaResult, type DiscoverOriginSchemaSuccess, type EndpointMethodAdvisory, GuidanceMode, type HttpMethod, type L2Result, type L2Route, type L3Result, type L4Result, type MetadataPreview, type MppPaymentOption, type NormalizedAccept, type NormalizedPaymentRequired, type OpenApiRoute, type OpenApiSource, type PaymentOption, type PricingMode, type ProbeResult, type TrustTier, VALIDATION_CODES, type ValidatePaymentRequiredDetailedResult, type ValidatePaymentRequiredOptions, type ValidationIssue, type ValidationSeverity, type ValidationStage, type ValidationSummary, type WellKnownRoute, type WellKnownSource, type X402PaymentOption, type X402V1PaymentOption, type X402V2PaymentOption, attachProbePayload, checkEndpointSchema, checkL2ForOpenAPI, checkL2ForWellknown, checkL4ForOpenAPI, checkL4ForWellknown, discoverOriginSchema, evaluateMetadataCompleteness, getL3, getL3ForOpenAPI, getL3ForProbe, getOpenAPI, getProbe, getWarningsFor402Body, getWarningsForL2, getWarningsForL3, getWarningsForL4, getWarningsForMppHeader, getWarningsForOpenAPI, getWarningsForWellKnown, getWellKnown, validatePaymentRequiredDetailed };
|
package/dist/index.d.ts
CHANGED
|
@@ -137,6 +137,11 @@ interface L3Result {
|
|
|
137
137
|
* and returned a 402. Used by getWarningsForL3 to run full payment-required validation.
|
|
138
138
|
*/
|
|
139
139
|
paymentRequiredBody?: unknown;
|
|
140
|
+
/**
|
|
141
|
+
* Raw WWW-Authenticate header value from the 402 response. Present when the endpoint
|
|
142
|
+
* was probed and returned a 402 with an MPP challenge. Used by getWarningsForMppHeader.
|
|
143
|
+
*/
|
|
144
|
+
wwwAuthenticate?: string;
|
|
140
145
|
}
|
|
141
146
|
interface L4Result {
|
|
142
147
|
guidance: string;
|
|
@@ -361,8 +366,21 @@ declare const AUDIT_CODES: {
|
|
|
361
366
|
readonly L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING";
|
|
362
367
|
readonly L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING";
|
|
363
368
|
readonly L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID";
|
|
369
|
+
readonly L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID";
|
|
364
370
|
readonly L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING";
|
|
365
371
|
readonly L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG";
|
|
372
|
+
readonly MPP_HEADER_MISSING: "MPP_HEADER_MISSING";
|
|
373
|
+
readonly MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES";
|
|
374
|
+
readonly MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING";
|
|
375
|
+
readonly MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING";
|
|
376
|
+
readonly MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING";
|
|
377
|
+
readonly MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING";
|
|
378
|
+
readonly MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING";
|
|
379
|
+
readonly MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING";
|
|
380
|
+
readonly MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID";
|
|
381
|
+
readonly MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING";
|
|
382
|
+
readonly MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING";
|
|
383
|
+
readonly MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING";
|
|
366
384
|
};
|
|
367
385
|
type AuditCode = (typeof AUDIT_CODES)[keyof typeof AUDIT_CODES];
|
|
368
386
|
|
|
@@ -392,4 +410,18 @@ declare function getWarningsForL3(l3: L3Result | null): AuditWarning[];
|
|
|
392
410
|
|
|
393
411
|
declare function getWarningsForL4(l4: L4Result | null): AuditWarning[];
|
|
394
412
|
|
|
395
|
-
|
|
413
|
+
/**
|
|
414
|
+
* Validates a raw WWW-Authenticate header value from an MPP 402 response and
|
|
415
|
+
* returns issues as AuditWarnings.
|
|
416
|
+
*
|
|
417
|
+
* Checks for:
|
|
418
|
+
* - Header presence
|
|
419
|
+
* - At least one Payment challenge
|
|
420
|
+
* - Required challenge parameters: id, method, intent, realm, expires, request
|
|
421
|
+
* - Valid base64url-encoded JSON in the request field
|
|
422
|
+
* - Required request fields: currency (asset), amount
|
|
423
|
+
* - Recommended request field: recipient (payTo)
|
|
424
|
+
*/
|
|
425
|
+
declare function getWarningsForMppHeader(wwwAuthenticate: string | null | undefined): AuditWarning[];
|
|
426
|
+
|
|
427
|
+
export { AUDIT_CODES, type AuditCode, type AuditSeverity, type AuditWarning, type AuthMode, type CheckEndpointNotFound, type CheckEndpointOptions, type CheckEndpointResult, type CheckEndpointSuccess, type DiscoverOriginSchemaNotFound, type DiscoverOriginSchemaOptions, type DiscoverOriginSchemaResult, type DiscoverOriginSchemaSuccess, type EndpointMethodAdvisory, GuidanceMode, type HttpMethod, type L2Result, type L2Route, type L3Result, type L4Result, type MetadataPreview, type MppPaymentOption, type NormalizedAccept, type NormalizedPaymentRequired, type OpenApiRoute, type OpenApiSource, type PaymentOption, type PricingMode, type ProbeResult, type TrustTier, VALIDATION_CODES, type ValidatePaymentRequiredDetailedResult, type ValidatePaymentRequiredOptions, type ValidationIssue, type ValidationSeverity, type ValidationStage, type ValidationSummary, type WellKnownRoute, type WellKnownSource, type X402PaymentOption, type X402V1PaymentOption, type X402V2PaymentOption, attachProbePayload, checkEndpointSchema, checkL2ForOpenAPI, checkL2ForWellknown, checkL4ForOpenAPI, checkL4ForWellknown, discoverOriginSchema, evaluateMetadataCompleteness, getL3, getL3ForOpenAPI, getL3ForProbe, getOpenAPI, getProbe, getWarningsFor402Body, getWarningsForL2, getWarningsForL3, getWarningsForL4, getWarningsForMppHeader, getWarningsForOpenAPI, getWarningsForWellKnown, getWellKnown, validatePaymentRequiredDetailed };
|
package/dist/index.js
CHANGED
|
@@ -14877,7 +14877,8 @@ function getL3ForProbe(probe, path, method) {
|
|
|
14877
14877
|
...inputSchema ? { inputSchema } : {},
|
|
14878
14878
|
...outputSchema ? { outputSchema } : {},
|
|
14879
14879
|
...paymentOptions.length ? { paymentOptions } : {},
|
|
14880
|
-
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
|
|
14880
|
+
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
|
|
14881
|
+
...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
|
|
14881
14882
|
};
|
|
14882
14883
|
}
|
|
14883
14884
|
async function attachProbePayload(url2, advisories) {
|
|
@@ -15058,9 +15059,23 @@ var AUDIT_CODES = {
|
|
|
15058
15059
|
L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
|
|
15059
15060
|
L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
|
|
15060
15061
|
L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
|
|
15062
|
+
L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
|
|
15061
15063
|
// ─── L4 guidance checks ──────────────────────────────────────────────────────
|
|
15062
15064
|
L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
|
|
15063
|
-
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
|
|
15065
|
+
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
|
|
15066
|
+
// ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
|
|
15067
|
+
MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
|
|
15068
|
+
MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
|
|
15069
|
+
MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
|
|
15070
|
+
MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
|
|
15071
|
+
MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
|
|
15072
|
+
MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
|
|
15073
|
+
MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
|
|
15074
|
+
MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
|
|
15075
|
+
MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
|
|
15076
|
+
MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
|
|
15077
|
+
MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
|
|
15078
|
+
MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
|
|
15064
15079
|
};
|
|
15065
15080
|
|
|
15066
15081
|
// src/core/protocols/x402/v1/coinbase-schema.ts
|
|
@@ -15149,6 +15164,157 @@ function validateWithCoinbaseSchema2(body) {
|
|
|
15149
15164
|
});
|
|
15150
15165
|
}
|
|
15151
15166
|
|
|
15167
|
+
// src/audit/warnings/mpp.ts
|
|
15168
|
+
function parseAuthParams2(segment) {
|
|
15169
|
+
const params = {};
|
|
15170
|
+
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
15171
|
+
let match;
|
|
15172
|
+
while ((match = re.exec(segment)) !== null) {
|
|
15173
|
+
params[match[1]] = match[2] ?? match[3] ?? "";
|
|
15174
|
+
}
|
|
15175
|
+
return params;
|
|
15176
|
+
}
|
|
15177
|
+
function getWarningsForMppHeader(wwwAuthenticate) {
|
|
15178
|
+
if (!wwwAuthenticate?.trim()) {
|
|
15179
|
+
return [
|
|
15180
|
+
{
|
|
15181
|
+
code: AUDIT_CODES.MPP_HEADER_MISSING,
|
|
15182
|
+
severity: "error",
|
|
15183
|
+
message: "WWW-Authenticate header is absent.",
|
|
15184
|
+
hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
|
|
15185
|
+
}
|
|
15186
|
+
];
|
|
15187
|
+
}
|
|
15188
|
+
const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
|
|
15189
|
+
if (segments.length === 0) {
|
|
15190
|
+
return [
|
|
15191
|
+
{
|
|
15192
|
+
code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
|
|
15193
|
+
severity: "error",
|
|
15194
|
+
message: "WWW-Authenticate header contains no Payment challenges.",
|
|
15195
|
+
hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
|
|
15196
|
+
}
|
|
15197
|
+
];
|
|
15198
|
+
}
|
|
15199
|
+
const warnings = [];
|
|
15200
|
+
for (let i = 0; i < segments.length; i++) {
|
|
15201
|
+
const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
|
|
15202
|
+
const params = parseAuthParams2(stripped);
|
|
15203
|
+
const idx = `WWW-Authenticate[${i}]`;
|
|
15204
|
+
if (!params["id"]) {
|
|
15205
|
+
warnings.push({
|
|
15206
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
|
|
15207
|
+
severity: "error",
|
|
15208
|
+
message: `Payment challenge ${i} is missing the id parameter.`,
|
|
15209
|
+
hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
|
|
15210
|
+
path: `${idx}.id`
|
|
15211
|
+
});
|
|
15212
|
+
}
|
|
15213
|
+
if (!params["method"]) {
|
|
15214
|
+
warnings.push({
|
|
15215
|
+
code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
|
|
15216
|
+
severity: "error",
|
|
15217
|
+
message: `Payment challenge ${i} is missing the method parameter.`,
|
|
15218
|
+
hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
|
|
15219
|
+
path: `${idx}.method`
|
|
15220
|
+
});
|
|
15221
|
+
}
|
|
15222
|
+
if (!params["intent"]) {
|
|
15223
|
+
warnings.push({
|
|
15224
|
+
code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
|
|
15225
|
+
severity: "error",
|
|
15226
|
+
message: `Payment challenge ${i} is missing the intent parameter.`,
|
|
15227
|
+
hint: 'Set intent="charge" on the Payment challenge.',
|
|
15228
|
+
path: `${idx}.intent`
|
|
15229
|
+
});
|
|
15230
|
+
}
|
|
15231
|
+
if (!params["realm"]) {
|
|
15232
|
+
warnings.push({
|
|
15233
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
|
|
15234
|
+
severity: "error",
|
|
15235
|
+
message: `Payment challenge ${i} is missing the realm parameter.`,
|
|
15236
|
+
hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
|
|
15237
|
+
path: `${idx}.realm`
|
|
15238
|
+
});
|
|
15239
|
+
}
|
|
15240
|
+
if (!params["expires"]) {
|
|
15241
|
+
warnings.push({
|
|
15242
|
+
code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
|
|
15243
|
+
severity: "error",
|
|
15244
|
+
message: `Payment challenge ${i} is missing the expires parameter.`,
|
|
15245
|
+
hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
|
|
15246
|
+
path: `${idx}.expires`
|
|
15247
|
+
});
|
|
15248
|
+
}
|
|
15249
|
+
const requestStr = params["request"];
|
|
15250
|
+
if (!requestStr) {
|
|
15251
|
+
warnings.push({
|
|
15252
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
|
|
15253
|
+
severity: "error",
|
|
15254
|
+
message: `Payment challenge ${i} is missing the request field.`,
|
|
15255
|
+
hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
|
|
15256
|
+
path: `${idx}.request`
|
|
15257
|
+
});
|
|
15258
|
+
continue;
|
|
15259
|
+
}
|
|
15260
|
+
let request;
|
|
15261
|
+
try {
|
|
15262
|
+
const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
|
|
15263
|
+
const parsed = JSON.parse(decoded);
|
|
15264
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
15265
|
+
throw new Error("not an object");
|
|
15266
|
+
}
|
|
15267
|
+
request = parsed;
|
|
15268
|
+
} catch {
|
|
15269
|
+
warnings.push({
|
|
15270
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
|
|
15271
|
+
severity: "error",
|
|
15272
|
+
message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
|
|
15273
|
+
hint: "The request value must be a base64url-encoded JCS JSON object.",
|
|
15274
|
+
path: `${idx}.request`
|
|
15275
|
+
});
|
|
15276
|
+
continue;
|
|
15277
|
+
}
|
|
15278
|
+
if (!request["currency"]) {
|
|
15279
|
+
warnings.push({
|
|
15280
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
|
|
15281
|
+
severity: "error",
|
|
15282
|
+
message: `Payment challenge ${i} is missing currency in the request object.`,
|
|
15283
|
+
hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
|
|
15284
|
+
path: `${idx}.request.currency`
|
|
15285
|
+
});
|
|
15286
|
+
}
|
|
15287
|
+
const amount = request["amount"];
|
|
15288
|
+
if (amount === void 0 || amount === null) {
|
|
15289
|
+
warnings.push({
|
|
15290
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
15291
|
+
severity: "error",
|
|
15292
|
+
message: `Payment challenge ${i} is missing amount in the request object.`,
|
|
15293
|
+
hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
|
|
15294
|
+
path: `${idx}.request.amount`
|
|
15295
|
+
});
|
|
15296
|
+
} else if (typeof amount !== "string" && typeof amount !== "number") {
|
|
15297
|
+
warnings.push({
|
|
15298
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
15299
|
+
severity: "error",
|
|
15300
|
+
message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
|
|
15301
|
+
hint: "Set amount to a raw token-unit string.",
|
|
15302
|
+
path: `${idx}.request.amount`
|
|
15303
|
+
});
|
|
15304
|
+
}
|
|
15305
|
+
if (!request["recipient"]) {
|
|
15306
|
+
warnings.push({
|
|
15307
|
+
code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
|
|
15308
|
+
severity: "error",
|
|
15309
|
+
message: `Payment challenge ${i} is missing recipient in the request object.`,
|
|
15310
|
+
hint: "Set recipient to the wallet address that should receive payment.",
|
|
15311
|
+
path: `${idx}.request.recipient`
|
|
15312
|
+
});
|
|
15313
|
+
}
|
|
15314
|
+
}
|
|
15315
|
+
return warnings;
|
|
15316
|
+
}
|
|
15317
|
+
|
|
15152
15318
|
// src/audit/warnings/l3.ts
|
|
15153
15319
|
function getWarningsFor402Body(body) {
|
|
15154
15320
|
if (!isRecord(body)) {
|
|
@@ -15278,17 +15444,28 @@ function getWarningsForL3(l3) {
|
|
|
15278
15444
|
hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
|
|
15279
15445
|
});
|
|
15280
15446
|
}
|
|
15281
|
-
if (l3.authMode === "paid" && !l3.protocols?.length) {
|
|
15447
|
+
if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
|
|
15282
15448
|
warnings.push({
|
|
15283
15449
|
code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
|
|
15284
15450
|
severity: "info",
|
|
15285
15451
|
message: "Paid endpoint does not declare supported payment protocols.",
|
|
15286
|
-
hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
|
|
15452
|
+
hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
|
|
15453
|
+
});
|
|
15454
|
+
}
|
|
15455
|
+
if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
|
|
15456
|
+
warnings.push({
|
|
15457
|
+
code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
|
|
15458
|
+
severity: "warn",
|
|
15459
|
+
message: "Paid endpoint did not return payment options in the 402 response.",
|
|
15460
|
+
hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
|
|
15287
15461
|
});
|
|
15288
15462
|
}
|
|
15289
15463
|
if (l3.paymentRequiredBody !== void 0) {
|
|
15290
15464
|
warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
|
|
15291
15465
|
}
|
|
15466
|
+
if (l3.wwwAuthenticate !== void 0) {
|
|
15467
|
+
warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
|
|
15468
|
+
}
|
|
15292
15469
|
return warnings;
|
|
15293
15470
|
}
|
|
15294
15471
|
|
|
@@ -15512,6 +15689,7 @@ export {
|
|
|
15512
15689
|
getWarningsForL2,
|
|
15513
15690
|
getWarningsForL3,
|
|
15514
15691
|
getWarningsForL4,
|
|
15692
|
+
getWarningsForMppHeader,
|
|
15515
15693
|
getWarningsForOpenAPI,
|
|
15516
15694
|
getWarningsForWellKnown,
|
|
15517
15695
|
getWellKnown,
|