@riddledc/riddle-proof 0.7.44 → 0.7.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/{chunk-G673RKAC.js → chunk-WSZ5AVKX.js} +144 -5
- package/dist/cli.cjs +144 -5
- package/dist/cli.js +1 -1
- package/dist/index.cjs +144 -5
- package/dist/index.js +1 -1
- package/dist/profile.cjs +144 -5
- package/dist/profile.d.cts +5 -0
- package/dist/profile.d.ts +5 -0
- package/dist/profile.js +1 -1
- package/dist/proof-run-engine.d.cts +3 -3
- package/dist/proof-run-engine.d.ts +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -223,6 +223,14 @@ cycle instead of reusing the final response, for example to repeat a fail-then-
|
|
|
223
223
|
success pair across multiple viewports. Repeated sequences also record
|
|
224
224
|
`sequence_cycle: true` after the first cycle.
|
|
225
225
|
|
|
226
|
+
Set `capture_request_body: true` to include compact request-body evidence on
|
|
227
|
+
mock hits. Add `request_body_contains` / `request_body_patterns` or
|
|
228
|
+
`request_body_not_contains` / `request_body_not_patterns` when the request body
|
|
229
|
+
is part of the contract, such as proving that a save request references the
|
|
230
|
+
current build ID returned by a prior mocked build response and not a stale one.
|
|
231
|
+
Body assertions use the full request body for matching and store only length
|
|
232
|
+
plus a compact sample in the proof evidence.
|
|
233
|
+
|
|
226
234
|
`target.setup_actions` is optional. Use it when the meaningful proof surface
|
|
227
235
|
appears only after a picker, tab, login stub, storage seed, form fill,
|
|
228
236
|
transport control, or other bounded interaction. Supported setup actions are
|
|
@@ -253,6 +253,24 @@ function normalizeNetworkMock(input, index) {
|
|
|
253
253
|
const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
|
|
254
254
|
const responsesInput = input.responses ?? input.sequence;
|
|
255
255
|
const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
|
|
256
|
+
const requestBodyContains = normalizeStringList(
|
|
257
|
+
input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
|
|
258
|
+
`target.network_mocks[${index}].request_body_contains`
|
|
259
|
+
);
|
|
260
|
+
const requestBodyPatterns = normalizeStringList(
|
|
261
|
+
input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
|
|
262
|
+
`target.network_mocks[${index}].request_body_patterns`
|
|
263
|
+
);
|
|
264
|
+
validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
|
|
265
|
+
const requestBodyNotContains = normalizeStringList(
|
|
266
|
+
input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
|
|
267
|
+
`target.network_mocks[${index}].request_body_not_contains`
|
|
268
|
+
);
|
|
269
|
+
const requestBodyNotPatterns = normalizeStringList(
|
|
270
|
+
input.request_body_not_patterns ?? input.requestBodyNotPatterns ?? input.request_body_forbidden_patterns ?? input.requestBodyForbiddenPatterns ?? input.body_not_patterns ?? input.bodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
|
|
271
|
+
`target.network_mocks[${index}].request_body_not_patterns`
|
|
272
|
+
);
|
|
273
|
+
validateRegexPatterns(requestBodyNotPatterns, `target.network_mocks[${index}].request_body_not_patterns`);
|
|
256
274
|
const requiredHitCount = numberValue(
|
|
257
275
|
input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
|
|
258
276
|
);
|
|
@@ -267,7 +285,12 @@ function normalizeNetworkMock(input, index) {
|
|
|
267
285
|
responses,
|
|
268
286
|
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
269
287
|
required_hit_count: requiredHitCount,
|
|
270
|
-
required: input.required === false ? false : true
|
|
288
|
+
required: input.required === false ? false : true,
|
|
289
|
+
capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length) || Boolean(requestBodyNotContains?.length) || Boolean(requestBodyNotPatterns?.length),
|
|
290
|
+
request_body_contains: requestBodyContains,
|
|
291
|
+
request_body_patterns: requestBodyPatterns,
|
|
292
|
+
request_body_not_contains: requestBodyNotContains,
|
|
293
|
+
request_body_not_patterns: requestBodyNotPatterns
|
|
271
294
|
};
|
|
272
295
|
}
|
|
273
296
|
function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
@@ -350,6 +373,16 @@ function normalizeStringList(value, label) {
|
|
|
350
373
|
if (!values.length) throw new Error(`${label} must contain non-empty strings.`);
|
|
351
374
|
return values;
|
|
352
375
|
}
|
|
376
|
+
function validateRegexPatterns(patterns, label) {
|
|
377
|
+
for (const pattern of patterns || []) {
|
|
378
|
+
try {
|
|
379
|
+
new RegExp(pattern);
|
|
380
|
+
} catch (error) {
|
|
381
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
382
|
+
throw new Error(`${label} contains invalid regex ${JSON.stringify(pattern)}: ${message}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
353
386
|
function normalizeCheck(input, index) {
|
|
354
387
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
355
388
|
const type = stringValue(input.type);
|
|
@@ -1020,6 +1053,17 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
1020
1053
|
reason: event.reason ?? event.error ?? "mock_failed"
|
|
1021
1054
|
});
|
|
1022
1055
|
}
|
|
1056
|
+
if (event.request_body_matches === false) {
|
|
1057
|
+
failed.push({
|
|
1058
|
+
label: event.label ?? null,
|
|
1059
|
+
url: event.url ?? null,
|
|
1060
|
+
method: event.method ?? null,
|
|
1061
|
+
reason: "request_body_mismatch",
|
|
1062
|
+
request_body_failures: event.request_body_failures ?? [],
|
|
1063
|
+
request_body_length: event.request_body_length ?? null,
|
|
1064
|
+
request_body_sample: event.request_body_sample ?? null
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1023
1067
|
}
|
|
1024
1068
|
return {
|
|
1025
1069
|
type: "network_mocks_succeeded",
|
|
@@ -1392,6 +1436,17 @@ function assessProfile(profile, evidence) {
|
|
|
1392
1436
|
reason: event.reason || event.error || "mock_failed",
|
|
1393
1437
|
});
|
|
1394
1438
|
}
|
|
1439
|
+
if (event && event.request_body_matches === false) {
|
|
1440
|
+
failed.push({
|
|
1441
|
+
label: event.label || null,
|
|
1442
|
+
url: event.url || null,
|
|
1443
|
+
method: event.method || null,
|
|
1444
|
+
reason: "request_body_mismatch",
|
|
1445
|
+
request_body_failures: event.request_body_failures || [],
|
|
1446
|
+
request_body_length: event.request_body_length || null,
|
|
1447
|
+
request_body_sample: event.request_body_sample || null,
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1395
1450
|
}
|
|
1396
1451
|
const hitsByLabel = {};
|
|
1397
1452
|
const requiredHitsByLabel = {};
|
|
@@ -1840,6 +1895,79 @@ function compactSetupResultText(value) {
|
|
|
1840
1895
|
if (text.length <= 500) return text;
|
|
1841
1896
|
return text.slice(0, 500) + "... (" + text.length + " chars)";
|
|
1842
1897
|
}
|
|
1898
|
+
function compactNetworkMockRequestBody(value) {
|
|
1899
|
+
const text = String(value || "").replace(/\s+/g, " ").trim();
|
|
1900
|
+
if (text.length <= 1000) return text;
|
|
1901
|
+
return text.slice(0, 1000) + "... (" + text.length + " chars)";
|
|
1902
|
+
}
|
|
1903
|
+
function networkMockStringList(value) {
|
|
1904
|
+
return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
|
|
1905
|
+
}
|
|
1906
|
+
function networkMockShouldCaptureRequestBody(mock) {
|
|
1907
|
+
return mock && (
|
|
1908
|
+
mock.capture_request_body === true
|
|
1909
|
+
|| networkMockStringList(mock.request_body_contains).length > 0
|
|
1910
|
+
|| networkMockStringList(mock.request_body_patterns).length > 0
|
|
1911
|
+
|| networkMockStringList(mock.request_body_not_contains).length > 0
|
|
1912
|
+
|| networkMockStringList(mock.request_body_not_patterns).length > 0
|
|
1913
|
+
);
|
|
1914
|
+
}
|
|
1915
|
+
function networkMockRequestBodyFailures(body, mock) {
|
|
1916
|
+
const failures = [];
|
|
1917
|
+
const rawBody = String(body || "");
|
|
1918
|
+
const compactBody = compactNetworkMockRequestBody(rawBody);
|
|
1919
|
+
for (const expected of networkMockStringList(mock.request_body_contains)) {
|
|
1920
|
+
if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
|
|
1921
|
+
failures.push({
|
|
1922
|
+
type: "request_body_missing_text",
|
|
1923
|
+
text: String(expected).slice(0, 200),
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
for (const pattern of networkMockStringList(mock.request_body_patterns)) {
|
|
1928
|
+
try {
|
|
1929
|
+
const regex = new RegExp(pattern);
|
|
1930
|
+
if (!regex.test(rawBody) && !regex.test(compactBody)) {
|
|
1931
|
+
failures.push({
|
|
1932
|
+
type: "request_body_pattern_not_matched",
|
|
1933
|
+
pattern: String(pattern).slice(0, 200),
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
} catch (error) {
|
|
1937
|
+
failures.push({
|
|
1938
|
+
type: "request_body_invalid_pattern",
|
|
1939
|
+
pattern: String(pattern).slice(0, 200),
|
|
1940
|
+
error: String(error && error.message ? error.message : error).slice(0, 500),
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
for (const forbidden of networkMockStringList(mock.request_body_not_contains)) {
|
|
1945
|
+
if (rawBody.includes(forbidden) || compactBody.includes(forbidden)) {
|
|
1946
|
+
failures.push({
|
|
1947
|
+
type: "request_body_forbidden_text",
|
|
1948
|
+
text: String(forbidden).slice(0, 200),
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
for (const pattern of networkMockStringList(mock.request_body_not_patterns)) {
|
|
1953
|
+
try {
|
|
1954
|
+
const regex = new RegExp(pattern);
|
|
1955
|
+
if (regex.test(rawBody) || regex.test(compactBody)) {
|
|
1956
|
+
failures.push({
|
|
1957
|
+
type: "request_body_forbidden_pattern_matched",
|
|
1958
|
+
pattern: String(pattern).slice(0, 200),
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
} catch (error) {
|
|
1962
|
+
failures.push({
|
|
1963
|
+
type: "request_body_invalid_pattern",
|
|
1964
|
+
pattern: String(pattern).slice(0, 200),
|
|
1965
|
+
error: String(error && error.message ? error.message : error).slice(0, 500),
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
return failures;
|
|
1970
|
+
}
|
|
1843
1971
|
async function setupLocatorVisible(locator, index) {
|
|
1844
1972
|
return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
|
|
1845
1973
|
}
|
|
@@ -1868,7 +1996,11 @@ async function registerNetworkMocks(mocks) {
|
|
|
1868
1996
|
body = JSON.stringify(response.body_json);
|
|
1869
1997
|
contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
|
|
1870
1998
|
}
|
|
1871
|
-
|
|
1999
|
+
const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock);
|
|
2000
|
+
const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
|
|
2001
|
+
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
|
|
2002
|
+
const status = response.status || mock.status || 200;
|
|
2003
|
+
const event = {
|
|
1872
2004
|
ok: true,
|
|
1873
2005
|
label: mock.label,
|
|
1874
2006
|
response_label: response.label || null,
|
|
@@ -1878,10 +2010,17 @@ async function registerNetworkMocks(mocks) {
|
|
|
1878
2010
|
sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
|
|
1879
2011
|
url: request.url(),
|
|
1880
2012
|
method,
|
|
1881
|
-
status
|
|
1882
|
-
}
|
|
2013
|
+
status,
|
|
2014
|
+
};
|
|
2015
|
+
if (shouldCaptureRequestBody) {
|
|
2016
|
+
event.request_body_matches = requestBodyFailures.length === 0;
|
|
2017
|
+
event.request_body_failures = requestBodyFailures;
|
|
2018
|
+
event.request_body_length = requestBody.length;
|
|
2019
|
+
event.request_body_sample = compactNetworkMockRequestBody(requestBody);
|
|
2020
|
+
}
|
|
2021
|
+
networkMockEvents.push(event);
|
|
1883
2022
|
await route.fulfill({
|
|
1884
|
-
status
|
|
2023
|
+
status,
|
|
1885
2024
|
headers,
|
|
1886
2025
|
contentType,
|
|
1887
2026
|
body,
|
package/dist/cli.cjs
CHANGED
|
@@ -7126,6 +7126,24 @@ function normalizeNetworkMock(input, index) {
|
|
|
7126
7126
|
const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
|
|
7127
7127
|
const responsesInput = input.responses ?? input.sequence;
|
|
7128
7128
|
const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
|
|
7129
|
+
const requestBodyContains = normalizeStringList(
|
|
7130
|
+
input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
|
|
7131
|
+
`target.network_mocks[${index}].request_body_contains`
|
|
7132
|
+
);
|
|
7133
|
+
const requestBodyPatterns = normalizeStringList(
|
|
7134
|
+
input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
|
|
7135
|
+
`target.network_mocks[${index}].request_body_patterns`
|
|
7136
|
+
);
|
|
7137
|
+
validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
|
|
7138
|
+
const requestBodyNotContains = normalizeStringList(
|
|
7139
|
+
input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
|
|
7140
|
+
`target.network_mocks[${index}].request_body_not_contains`
|
|
7141
|
+
);
|
|
7142
|
+
const requestBodyNotPatterns = normalizeStringList(
|
|
7143
|
+
input.request_body_not_patterns ?? input.requestBodyNotPatterns ?? input.request_body_forbidden_patterns ?? input.requestBodyForbiddenPatterns ?? input.body_not_patterns ?? input.bodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
|
|
7144
|
+
`target.network_mocks[${index}].request_body_not_patterns`
|
|
7145
|
+
);
|
|
7146
|
+
validateRegexPatterns(requestBodyNotPatterns, `target.network_mocks[${index}].request_body_not_patterns`);
|
|
7129
7147
|
const requiredHitCount = numberValue(
|
|
7130
7148
|
input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
|
|
7131
7149
|
);
|
|
@@ -7140,7 +7158,12 @@ function normalizeNetworkMock(input, index) {
|
|
|
7140
7158
|
responses,
|
|
7141
7159
|
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
7142
7160
|
required_hit_count: requiredHitCount,
|
|
7143
|
-
required: input.required === false ? false : true
|
|
7161
|
+
required: input.required === false ? false : true,
|
|
7162
|
+
capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length) || Boolean(requestBodyNotContains?.length) || Boolean(requestBodyNotPatterns?.length),
|
|
7163
|
+
request_body_contains: requestBodyContains,
|
|
7164
|
+
request_body_patterns: requestBodyPatterns,
|
|
7165
|
+
request_body_not_contains: requestBodyNotContains,
|
|
7166
|
+
request_body_not_patterns: requestBodyNotPatterns
|
|
7144
7167
|
};
|
|
7145
7168
|
}
|
|
7146
7169
|
function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
@@ -7223,6 +7246,16 @@ function normalizeStringList(value, label) {
|
|
|
7223
7246
|
if (!values.length) throw new Error(`${label} must contain non-empty strings.`);
|
|
7224
7247
|
return values;
|
|
7225
7248
|
}
|
|
7249
|
+
function validateRegexPatterns(patterns, label) {
|
|
7250
|
+
for (const pattern of patterns || []) {
|
|
7251
|
+
try {
|
|
7252
|
+
new RegExp(pattern);
|
|
7253
|
+
} catch (error) {
|
|
7254
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7255
|
+
throw new Error(`${label} contains invalid regex ${JSON.stringify(pattern)}: ${message}`);
|
|
7256
|
+
}
|
|
7257
|
+
}
|
|
7258
|
+
}
|
|
7226
7259
|
function normalizeCheck(input, index) {
|
|
7227
7260
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
7228
7261
|
const type = stringValue2(input.type);
|
|
@@ -7893,6 +7926,17 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
7893
7926
|
reason: event.reason ?? event.error ?? "mock_failed"
|
|
7894
7927
|
});
|
|
7895
7928
|
}
|
|
7929
|
+
if (event.request_body_matches === false) {
|
|
7930
|
+
failed.push({
|
|
7931
|
+
label: event.label ?? null,
|
|
7932
|
+
url: event.url ?? null,
|
|
7933
|
+
method: event.method ?? null,
|
|
7934
|
+
reason: "request_body_mismatch",
|
|
7935
|
+
request_body_failures: event.request_body_failures ?? [],
|
|
7936
|
+
request_body_length: event.request_body_length ?? null,
|
|
7937
|
+
request_body_sample: event.request_body_sample ?? null
|
|
7938
|
+
});
|
|
7939
|
+
}
|
|
7896
7940
|
}
|
|
7897
7941
|
return {
|
|
7898
7942
|
type: "network_mocks_succeeded",
|
|
@@ -8249,6 +8293,17 @@ function assessProfile(profile, evidence) {
|
|
|
8249
8293
|
reason: event.reason || event.error || "mock_failed",
|
|
8250
8294
|
});
|
|
8251
8295
|
}
|
|
8296
|
+
if (event && event.request_body_matches === false) {
|
|
8297
|
+
failed.push({
|
|
8298
|
+
label: event.label || null,
|
|
8299
|
+
url: event.url || null,
|
|
8300
|
+
method: event.method || null,
|
|
8301
|
+
reason: "request_body_mismatch",
|
|
8302
|
+
request_body_failures: event.request_body_failures || [],
|
|
8303
|
+
request_body_length: event.request_body_length || null,
|
|
8304
|
+
request_body_sample: event.request_body_sample || null,
|
|
8305
|
+
});
|
|
8306
|
+
}
|
|
8252
8307
|
}
|
|
8253
8308
|
const hitsByLabel = {};
|
|
8254
8309
|
const requiredHitsByLabel = {};
|
|
@@ -8697,6 +8752,79 @@ function compactSetupResultText(value) {
|
|
|
8697
8752
|
if (text.length <= 500) return text;
|
|
8698
8753
|
return text.slice(0, 500) + "... (" + text.length + " chars)";
|
|
8699
8754
|
}
|
|
8755
|
+
function compactNetworkMockRequestBody(value) {
|
|
8756
|
+
const text = String(value || "").replace(/\s+/g, " ").trim();
|
|
8757
|
+
if (text.length <= 1000) return text;
|
|
8758
|
+
return text.slice(0, 1000) + "... (" + text.length + " chars)";
|
|
8759
|
+
}
|
|
8760
|
+
function networkMockStringList(value) {
|
|
8761
|
+
return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
|
|
8762
|
+
}
|
|
8763
|
+
function networkMockShouldCaptureRequestBody(mock) {
|
|
8764
|
+
return mock && (
|
|
8765
|
+
mock.capture_request_body === true
|
|
8766
|
+
|| networkMockStringList(mock.request_body_contains).length > 0
|
|
8767
|
+
|| networkMockStringList(mock.request_body_patterns).length > 0
|
|
8768
|
+
|| networkMockStringList(mock.request_body_not_contains).length > 0
|
|
8769
|
+
|| networkMockStringList(mock.request_body_not_patterns).length > 0
|
|
8770
|
+
);
|
|
8771
|
+
}
|
|
8772
|
+
function networkMockRequestBodyFailures(body, mock) {
|
|
8773
|
+
const failures = [];
|
|
8774
|
+
const rawBody = String(body || "");
|
|
8775
|
+
const compactBody = compactNetworkMockRequestBody(rawBody);
|
|
8776
|
+
for (const expected of networkMockStringList(mock.request_body_contains)) {
|
|
8777
|
+
if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
|
|
8778
|
+
failures.push({
|
|
8779
|
+
type: "request_body_missing_text",
|
|
8780
|
+
text: String(expected).slice(0, 200),
|
|
8781
|
+
});
|
|
8782
|
+
}
|
|
8783
|
+
}
|
|
8784
|
+
for (const pattern of networkMockStringList(mock.request_body_patterns)) {
|
|
8785
|
+
try {
|
|
8786
|
+
const regex = new RegExp(pattern);
|
|
8787
|
+
if (!regex.test(rawBody) && !regex.test(compactBody)) {
|
|
8788
|
+
failures.push({
|
|
8789
|
+
type: "request_body_pattern_not_matched",
|
|
8790
|
+
pattern: String(pattern).slice(0, 200),
|
|
8791
|
+
});
|
|
8792
|
+
}
|
|
8793
|
+
} catch (error) {
|
|
8794
|
+
failures.push({
|
|
8795
|
+
type: "request_body_invalid_pattern",
|
|
8796
|
+
pattern: String(pattern).slice(0, 200),
|
|
8797
|
+
error: String(error && error.message ? error.message : error).slice(0, 500),
|
|
8798
|
+
});
|
|
8799
|
+
}
|
|
8800
|
+
}
|
|
8801
|
+
for (const forbidden of networkMockStringList(mock.request_body_not_contains)) {
|
|
8802
|
+
if (rawBody.includes(forbidden) || compactBody.includes(forbidden)) {
|
|
8803
|
+
failures.push({
|
|
8804
|
+
type: "request_body_forbidden_text",
|
|
8805
|
+
text: String(forbidden).slice(0, 200),
|
|
8806
|
+
});
|
|
8807
|
+
}
|
|
8808
|
+
}
|
|
8809
|
+
for (const pattern of networkMockStringList(mock.request_body_not_patterns)) {
|
|
8810
|
+
try {
|
|
8811
|
+
const regex = new RegExp(pattern);
|
|
8812
|
+
if (regex.test(rawBody) || regex.test(compactBody)) {
|
|
8813
|
+
failures.push({
|
|
8814
|
+
type: "request_body_forbidden_pattern_matched",
|
|
8815
|
+
pattern: String(pattern).slice(0, 200),
|
|
8816
|
+
});
|
|
8817
|
+
}
|
|
8818
|
+
} catch (error) {
|
|
8819
|
+
failures.push({
|
|
8820
|
+
type: "request_body_invalid_pattern",
|
|
8821
|
+
pattern: String(pattern).slice(0, 200),
|
|
8822
|
+
error: String(error && error.message ? error.message : error).slice(0, 500),
|
|
8823
|
+
});
|
|
8824
|
+
}
|
|
8825
|
+
}
|
|
8826
|
+
return failures;
|
|
8827
|
+
}
|
|
8700
8828
|
async function setupLocatorVisible(locator, index) {
|
|
8701
8829
|
return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
|
|
8702
8830
|
}
|
|
@@ -8725,7 +8853,11 @@ async function registerNetworkMocks(mocks) {
|
|
|
8725
8853
|
body = JSON.stringify(response.body_json);
|
|
8726
8854
|
contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
|
|
8727
8855
|
}
|
|
8728
|
-
|
|
8856
|
+
const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock);
|
|
8857
|
+
const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
|
|
8858
|
+
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
|
|
8859
|
+
const status = response.status || mock.status || 200;
|
|
8860
|
+
const event = {
|
|
8729
8861
|
ok: true,
|
|
8730
8862
|
label: mock.label,
|
|
8731
8863
|
response_label: response.label || null,
|
|
@@ -8735,10 +8867,17 @@ async function registerNetworkMocks(mocks) {
|
|
|
8735
8867
|
sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
|
|
8736
8868
|
url: request.url(),
|
|
8737
8869
|
method,
|
|
8738
|
-
status
|
|
8739
|
-
}
|
|
8870
|
+
status,
|
|
8871
|
+
};
|
|
8872
|
+
if (shouldCaptureRequestBody) {
|
|
8873
|
+
event.request_body_matches = requestBodyFailures.length === 0;
|
|
8874
|
+
event.request_body_failures = requestBodyFailures;
|
|
8875
|
+
event.request_body_length = requestBody.length;
|
|
8876
|
+
event.request_body_sample = compactNetworkMockRequestBody(requestBody);
|
|
8877
|
+
}
|
|
8878
|
+
networkMockEvents.push(event);
|
|
8740
8879
|
await route.fulfill({
|
|
8741
|
-
status
|
|
8880
|
+
status,
|
|
8742
8881
|
headers,
|
|
8743
8882
|
contentType,
|
|
8744
8883
|
body,
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -8967,6 +8967,24 @@ function normalizeNetworkMock(input, index) {
|
|
|
8967
8967
|
const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
|
|
8968
8968
|
const responsesInput = input.responses ?? input.sequence;
|
|
8969
8969
|
const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
|
|
8970
|
+
const requestBodyContains = normalizeStringList(
|
|
8971
|
+
input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
|
|
8972
|
+
`target.network_mocks[${index}].request_body_contains`
|
|
8973
|
+
);
|
|
8974
|
+
const requestBodyPatterns = normalizeStringList(
|
|
8975
|
+
input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
|
|
8976
|
+
`target.network_mocks[${index}].request_body_patterns`
|
|
8977
|
+
);
|
|
8978
|
+
validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
|
|
8979
|
+
const requestBodyNotContains = normalizeStringList(
|
|
8980
|
+
input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
|
|
8981
|
+
`target.network_mocks[${index}].request_body_not_contains`
|
|
8982
|
+
);
|
|
8983
|
+
const requestBodyNotPatterns = normalizeStringList(
|
|
8984
|
+
input.request_body_not_patterns ?? input.requestBodyNotPatterns ?? input.request_body_forbidden_patterns ?? input.requestBodyForbiddenPatterns ?? input.body_not_patterns ?? input.bodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
|
|
8985
|
+
`target.network_mocks[${index}].request_body_not_patterns`
|
|
8986
|
+
);
|
|
8987
|
+
validateRegexPatterns(requestBodyNotPatterns, `target.network_mocks[${index}].request_body_not_patterns`);
|
|
8970
8988
|
const requiredHitCount = numberValue3(
|
|
8971
8989
|
input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
|
|
8972
8990
|
);
|
|
@@ -8981,7 +8999,12 @@ function normalizeNetworkMock(input, index) {
|
|
|
8981
8999
|
responses,
|
|
8982
9000
|
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
8983
9001
|
required_hit_count: requiredHitCount,
|
|
8984
|
-
required: input.required === false ? false : true
|
|
9002
|
+
required: input.required === false ? false : true,
|
|
9003
|
+
capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length) || Boolean(requestBodyNotContains?.length) || Boolean(requestBodyNotPatterns?.length),
|
|
9004
|
+
request_body_contains: requestBodyContains,
|
|
9005
|
+
request_body_patterns: requestBodyPatterns,
|
|
9006
|
+
request_body_not_contains: requestBodyNotContains,
|
|
9007
|
+
request_body_not_patterns: requestBodyNotPatterns
|
|
8985
9008
|
};
|
|
8986
9009
|
}
|
|
8987
9010
|
function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
@@ -9064,6 +9087,16 @@ function normalizeStringList(value, label) {
|
|
|
9064
9087
|
if (!values.length) throw new Error(`${label} must contain non-empty strings.`);
|
|
9065
9088
|
return values;
|
|
9066
9089
|
}
|
|
9090
|
+
function validateRegexPatterns(patterns, label) {
|
|
9091
|
+
for (const pattern of patterns || []) {
|
|
9092
|
+
try {
|
|
9093
|
+
new RegExp(pattern);
|
|
9094
|
+
} catch (error) {
|
|
9095
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9096
|
+
throw new Error(`${label} contains invalid regex ${JSON.stringify(pattern)}: ${message}`);
|
|
9097
|
+
}
|
|
9098
|
+
}
|
|
9099
|
+
}
|
|
9067
9100
|
function normalizeCheck(input, index) {
|
|
9068
9101
|
if (!isRecord2(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
9069
9102
|
const type = stringValue5(input.type);
|
|
@@ -9734,6 +9767,17 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
9734
9767
|
reason: event.reason ?? event.error ?? "mock_failed"
|
|
9735
9768
|
});
|
|
9736
9769
|
}
|
|
9770
|
+
if (event.request_body_matches === false) {
|
|
9771
|
+
failed.push({
|
|
9772
|
+
label: event.label ?? null,
|
|
9773
|
+
url: event.url ?? null,
|
|
9774
|
+
method: event.method ?? null,
|
|
9775
|
+
reason: "request_body_mismatch",
|
|
9776
|
+
request_body_failures: event.request_body_failures ?? [],
|
|
9777
|
+
request_body_length: event.request_body_length ?? null,
|
|
9778
|
+
request_body_sample: event.request_body_sample ?? null
|
|
9779
|
+
});
|
|
9780
|
+
}
|
|
9737
9781
|
}
|
|
9738
9782
|
return {
|
|
9739
9783
|
type: "network_mocks_succeeded",
|
|
@@ -10106,6 +10150,17 @@ function assessProfile(profile, evidence) {
|
|
|
10106
10150
|
reason: event.reason || event.error || "mock_failed",
|
|
10107
10151
|
});
|
|
10108
10152
|
}
|
|
10153
|
+
if (event && event.request_body_matches === false) {
|
|
10154
|
+
failed.push({
|
|
10155
|
+
label: event.label || null,
|
|
10156
|
+
url: event.url || null,
|
|
10157
|
+
method: event.method || null,
|
|
10158
|
+
reason: "request_body_mismatch",
|
|
10159
|
+
request_body_failures: event.request_body_failures || [],
|
|
10160
|
+
request_body_length: event.request_body_length || null,
|
|
10161
|
+
request_body_sample: event.request_body_sample || null,
|
|
10162
|
+
});
|
|
10163
|
+
}
|
|
10109
10164
|
}
|
|
10110
10165
|
const hitsByLabel = {};
|
|
10111
10166
|
const requiredHitsByLabel = {};
|
|
@@ -10554,6 +10609,79 @@ function compactSetupResultText(value) {
|
|
|
10554
10609
|
if (text.length <= 500) return text;
|
|
10555
10610
|
return text.slice(0, 500) + "... (" + text.length + " chars)";
|
|
10556
10611
|
}
|
|
10612
|
+
function compactNetworkMockRequestBody(value) {
|
|
10613
|
+
const text = String(value || "").replace(/\s+/g, " ").trim();
|
|
10614
|
+
if (text.length <= 1000) return text;
|
|
10615
|
+
return text.slice(0, 1000) + "... (" + text.length + " chars)";
|
|
10616
|
+
}
|
|
10617
|
+
function networkMockStringList(value) {
|
|
10618
|
+
return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
|
|
10619
|
+
}
|
|
10620
|
+
function networkMockShouldCaptureRequestBody(mock) {
|
|
10621
|
+
return mock && (
|
|
10622
|
+
mock.capture_request_body === true
|
|
10623
|
+
|| networkMockStringList(mock.request_body_contains).length > 0
|
|
10624
|
+
|| networkMockStringList(mock.request_body_patterns).length > 0
|
|
10625
|
+
|| networkMockStringList(mock.request_body_not_contains).length > 0
|
|
10626
|
+
|| networkMockStringList(mock.request_body_not_patterns).length > 0
|
|
10627
|
+
);
|
|
10628
|
+
}
|
|
10629
|
+
function networkMockRequestBodyFailures(body, mock) {
|
|
10630
|
+
const failures = [];
|
|
10631
|
+
const rawBody = String(body || "");
|
|
10632
|
+
const compactBody = compactNetworkMockRequestBody(rawBody);
|
|
10633
|
+
for (const expected of networkMockStringList(mock.request_body_contains)) {
|
|
10634
|
+
if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
|
|
10635
|
+
failures.push({
|
|
10636
|
+
type: "request_body_missing_text",
|
|
10637
|
+
text: String(expected).slice(0, 200),
|
|
10638
|
+
});
|
|
10639
|
+
}
|
|
10640
|
+
}
|
|
10641
|
+
for (const pattern of networkMockStringList(mock.request_body_patterns)) {
|
|
10642
|
+
try {
|
|
10643
|
+
const regex = new RegExp(pattern);
|
|
10644
|
+
if (!regex.test(rawBody) && !regex.test(compactBody)) {
|
|
10645
|
+
failures.push({
|
|
10646
|
+
type: "request_body_pattern_not_matched",
|
|
10647
|
+
pattern: String(pattern).slice(0, 200),
|
|
10648
|
+
});
|
|
10649
|
+
}
|
|
10650
|
+
} catch (error) {
|
|
10651
|
+
failures.push({
|
|
10652
|
+
type: "request_body_invalid_pattern",
|
|
10653
|
+
pattern: String(pattern).slice(0, 200),
|
|
10654
|
+
error: String(error && error.message ? error.message : error).slice(0, 500),
|
|
10655
|
+
});
|
|
10656
|
+
}
|
|
10657
|
+
}
|
|
10658
|
+
for (const forbidden of networkMockStringList(mock.request_body_not_contains)) {
|
|
10659
|
+
if (rawBody.includes(forbidden) || compactBody.includes(forbidden)) {
|
|
10660
|
+
failures.push({
|
|
10661
|
+
type: "request_body_forbidden_text",
|
|
10662
|
+
text: String(forbidden).slice(0, 200),
|
|
10663
|
+
});
|
|
10664
|
+
}
|
|
10665
|
+
}
|
|
10666
|
+
for (const pattern of networkMockStringList(mock.request_body_not_patterns)) {
|
|
10667
|
+
try {
|
|
10668
|
+
const regex = new RegExp(pattern);
|
|
10669
|
+
if (regex.test(rawBody) || regex.test(compactBody)) {
|
|
10670
|
+
failures.push({
|
|
10671
|
+
type: "request_body_forbidden_pattern_matched",
|
|
10672
|
+
pattern: String(pattern).slice(0, 200),
|
|
10673
|
+
});
|
|
10674
|
+
}
|
|
10675
|
+
} catch (error) {
|
|
10676
|
+
failures.push({
|
|
10677
|
+
type: "request_body_invalid_pattern",
|
|
10678
|
+
pattern: String(pattern).slice(0, 200),
|
|
10679
|
+
error: String(error && error.message ? error.message : error).slice(0, 500),
|
|
10680
|
+
});
|
|
10681
|
+
}
|
|
10682
|
+
}
|
|
10683
|
+
return failures;
|
|
10684
|
+
}
|
|
10557
10685
|
async function setupLocatorVisible(locator, index) {
|
|
10558
10686
|
return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
|
|
10559
10687
|
}
|
|
@@ -10582,7 +10710,11 @@ async function registerNetworkMocks(mocks) {
|
|
|
10582
10710
|
body = JSON.stringify(response.body_json);
|
|
10583
10711
|
contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
|
|
10584
10712
|
}
|
|
10585
|
-
|
|
10713
|
+
const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock);
|
|
10714
|
+
const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
|
|
10715
|
+
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
|
|
10716
|
+
const status = response.status || mock.status || 200;
|
|
10717
|
+
const event = {
|
|
10586
10718
|
ok: true,
|
|
10587
10719
|
label: mock.label,
|
|
10588
10720
|
response_label: response.label || null,
|
|
@@ -10592,10 +10724,17 @@ async function registerNetworkMocks(mocks) {
|
|
|
10592
10724
|
sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
|
|
10593
10725
|
url: request.url(),
|
|
10594
10726
|
method,
|
|
10595
|
-
status
|
|
10596
|
-
}
|
|
10727
|
+
status,
|
|
10728
|
+
};
|
|
10729
|
+
if (shouldCaptureRequestBody) {
|
|
10730
|
+
event.request_body_matches = requestBodyFailures.length === 0;
|
|
10731
|
+
event.request_body_failures = requestBodyFailures;
|
|
10732
|
+
event.request_body_length = requestBody.length;
|
|
10733
|
+
event.request_body_sample = compactNetworkMockRequestBody(requestBody);
|
|
10734
|
+
}
|
|
10735
|
+
networkMockEvents.push(event);
|
|
10597
10736
|
await route.fulfill({
|
|
10598
|
-
status
|
|
10737
|
+
status,
|
|
10599
10738
|
headers,
|
|
10600
10739
|
contentType,
|
|
10601
10740
|
body,
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
resolveRiddleProofProfileTimeoutSec,
|
|
59
59
|
slugifyRiddleProofProfileName,
|
|
60
60
|
summarizeRiddleProofProfileResult
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-WSZ5AVKX.js";
|
|
62
62
|
import {
|
|
63
63
|
DEFAULT_RIDDLE_API_BASE_URL,
|
|
64
64
|
DEFAULT_RIDDLE_API_KEY_FILE,
|
package/dist/profile.cjs
CHANGED
|
@@ -296,6 +296,24 @@ function normalizeNetworkMock(input, index) {
|
|
|
296
296
|
const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
|
|
297
297
|
const responsesInput = input.responses ?? input.sequence;
|
|
298
298
|
const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
|
|
299
|
+
const requestBodyContains = normalizeStringList(
|
|
300
|
+
input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
|
|
301
|
+
`target.network_mocks[${index}].request_body_contains`
|
|
302
|
+
);
|
|
303
|
+
const requestBodyPatterns = normalizeStringList(
|
|
304
|
+
input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
|
|
305
|
+
`target.network_mocks[${index}].request_body_patterns`
|
|
306
|
+
);
|
|
307
|
+
validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
|
|
308
|
+
const requestBodyNotContains = normalizeStringList(
|
|
309
|
+
input.request_body_not_contains ?? input.requestBodyNotContains ?? input.request_body_absent ?? input.requestBodyAbsent ?? input.body_not_contains ?? input.bodyNotContains ?? input.body_absent ?? input.bodyAbsent,
|
|
310
|
+
`target.network_mocks[${index}].request_body_not_contains`
|
|
311
|
+
);
|
|
312
|
+
const requestBodyNotPatterns = normalizeStringList(
|
|
313
|
+
input.request_body_not_patterns ?? input.requestBodyNotPatterns ?? input.request_body_forbidden_patterns ?? input.requestBodyForbiddenPatterns ?? input.body_not_patterns ?? input.bodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
|
|
314
|
+
`target.network_mocks[${index}].request_body_not_patterns`
|
|
315
|
+
);
|
|
316
|
+
validateRegexPatterns(requestBodyNotPatterns, `target.network_mocks[${index}].request_body_not_patterns`);
|
|
299
317
|
const requiredHitCount = numberValue(
|
|
300
318
|
input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
|
|
301
319
|
);
|
|
@@ -310,7 +328,12 @@ function normalizeNetworkMock(input, index) {
|
|
|
310
328
|
responses,
|
|
311
329
|
repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
|
|
312
330
|
required_hit_count: requiredHitCount,
|
|
313
|
-
required: input.required === false ? false : true
|
|
331
|
+
required: input.required === false ? false : true,
|
|
332
|
+
capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length) || Boolean(requestBodyNotContains?.length) || Boolean(requestBodyNotPatterns?.length),
|
|
333
|
+
request_body_contains: requestBodyContains,
|
|
334
|
+
request_body_patterns: requestBodyPatterns,
|
|
335
|
+
request_body_not_contains: requestBodyNotContains,
|
|
336
|
+
request_body_not_patterns: requestBodyNotPatterns
|
|
314
337
|
};
|
|
315
338
|
}
|
|
316
339
|
function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
|
|
@@ -393,6 +416,16 @@ function normalizeStringList(value, label) {
|
|
|
393
416
|
if (!values.length) throw new Error(`${label} must contain non-empty strings.`);
|
|
394
417
|
return values;
|
|
395
418
|
}
|
|
419
|
+
function validateRegexPatterns(patterns, label) {
|
|
420
|
+
for (const pattern of patterns || []) {
|
|
421
|
+
try {
|
|
422
|
+
new RegExp(pattern);
|
|
423
|
+
} catch (error) {
|
|
424
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
425
|
+
throw new Error(`${label} contains invalid regex ${JSON.stringify(pattern)}: ${message}`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
396
429
|
function normalizeCheck(input, index) {
|
|
397
430
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
398
431
|
const type = stringValue(input.type);
|
|
@@ -1063,6 +1096,17 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
|
|
|
1063
1096
|
reason: event.reason ?? event.error ?? "mock_failed"
|
|
1064
1097
|
});
|
|
1065
1098
|
}
|
|
1099
|
+
if (event.request_body_matches === false) {
|
|
1100
|
+
failed.push({
|
|
1101
|
+
label: event.label ?? null,
|
|
1102
|
+
url: event.url ?? null,
|
|
1103
|
+
method: event.method ?? null,
|
|
1104
|
+
reason: "request_body_mismatch",
|
|
1105
|
+
request_body_failures: event.request_body_failures ?? [],
|
|
1106
|
+
request_body_length: event.request_body_length ?? null,
|
|
1107
|
+
request_body_sample: event.request_body_sample ?? null
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1066
1110
|
}
|
|
1067
1111
|
return {
|
|
1068
1112
|
type: "network_mocks_succeeded",
|
|
@@ -1435,6 +1479,17 @@ function assessProfile(profile, evidence) {
|
|
|
1435
1479
|
reason: event.reason || event.error || "mock_failed",
|
|
1436
1480
|
});
|
|
1437
1481
|
}
|
|
1482
|
+
if (event && event.request_body_matches === false) {
|
|
1483
|
+
failed.push({
|
|
1484
|
+
label: event.label || null,
|
|
1485
|
+
url: event.url || null,
|
|
1486
|
+
method: event.method || null,
|
|
1487
|
+
reason: "request_body_mismatch",
|
|
1488
|
+
request_body_failures: event.request_body_failures || [],
|
|
1489
|
+
request_body_length: event.request_body_length || null,
|
|
1490
|
+
request_body_sample: event.request_body_sample || null,
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1438
1493
|
}
|
|
1439
1494
|
const hitsByLabel = {};
|
|
1440
1495
|
const requiredHitsByLabel = {};
|
|
@@ -1883,6 +1938,79 @@ function compactSetupResultText(value) {
|
|
|
1883
1938
|
if (text.length <= 500) return text;
|
|
1884
1939
|
return text.slice(0, 500) + "... (" + text.length + " chars)";
|
|
1885
1940
|
}
|
|
1941
|
+
function compactNetworkMockRequestBody(value) {
|
|
1942
|
+
const text = String(value || "").replace(/\s+/g, " ").trim();
|
|
1943
|
+
if (text.length <= 1000) return text;
|
|
1944
|
+
return text.slice(0, 1000) + "... (" + text.length + " chars)";
|
|
1945
|
+
}
|
|
1946
|
+
function networkMockStringList(value) {
|
|
1947
|
+
return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
|
|
1948
|
+
}
|
|
1949
|
+
function networkMockShouldCaptureRequestBody(mock) {
|
|
1950
|
+
return mock && (
|
|
1951
|
+
mock.capture_request_body === true
|
|
1952
|
+
|| networkMockStringList(mock.request_body_contains).length > 0
|
|
1953
|
+
|| networkMockStringList(mock.request_body_patterns).length > 0
|
|
1954
|
+
|| networkMockStringList(mock.request_body_not_contains).length > 0
|
|
1955
|
+
|| networkMockStringList(mock.request_body_not_patterns).length > 0
|
|
1956
|
+
);
|
|
1957
|
+
}
|
|
1958
|
+
function networkMockRequestBodyFailures(body, mock) {
|
|
1959
|
+
const failures = [];
|
|
1960
|
+
const rawBody = String(body || "");
|
|
1961
|
+
const compactBody = compactNetworkMockRequestBody(rawBody);
|
|
1962
|
+
for (const expected of networkMockStringList(mock.request_body_contains)) {
|
|
1963
|
+
if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
|
|
1964
|
+
failures.push({
|
|
1965
|
+
type: "request_body_missing_text",
|
|
1966
|
+
text: String(expected).slice(0, 200),
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
for (const pattern of networkMockStringList(mock.request_body_patterns)) {
|
|
1971
|
+
try {
|
|
1972
|
+
const regex = new RegExp(pattern);
|
|
1973
|
+
if (!regex.test(rawBody) && !regex.test(compactBody)) {
|
|
1974
|
+
failures.push({
|
|
1975
|
+
type: "request_body_pattern_not_matched",
|
|
1976
|
+
pattern: String(pattern).slice(0, 200),
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
} catch (error) {
|
|
1980
|
+
failures.push({
|
|
1981
|
+
type: "request_body_invalid_pattern",
|
|
1982
|
+
pattern: String(pattern).slice(0, 200),
|
|
1983
|
+
error: String(error && error.message ? error.message : error).slice(0, 500),
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
for (const forbidden of networkMockStringList(mock.request_body_not_contains)) {
|
|
1988
|
+
if (rawBody.includes(forbidden) || compactBody.includes(forbidden)) {
|
|
1989
|
+
failures.push({
|
|
1990
|
+
type: "request_body_forbidden_text",
|
|
1991
|
+
text: String(forbidden).slice(0, 200),
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
for (const pattern of networkMockStringList(mock.request_body_not_patterns)) {
|
|
1996
|
+
try {
|
|
1997
|
+
const regex = new RegExp(pattern);
|
|
1998
|
+
if (regex.test(rawBody) || regex.test(compactBody)) {
|
|
1999
|
+
failures.push({
|
|
2000
|
+
type: "request_body_forbidden_pattern_matched",
|
|
2001
|
+
pattern: String(pattern).slice(0, 200),
|
|
2002
|
+
});
|
|
2003
|
+
}
|
|
2004
|
+
} catch (error) {
|
|
2005
|
+
failures.push({
|
|
2006
|
+
type: "request_body_invalid_pattern",
|
|
2007
|
+
pattern: String(pattern).slice(0, 200),
|
|
2008
|
+
error: String(error && error.message ? error.message : error).slice(0, 500),
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
return failures;
|
|
2013
|
+
}
|
|
1886
2014
|
async function setupLocatorVisible(locator, index) {
|
|
1887
2015
|
return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
|
|
1888
2016
|
}
|
|
@@ -1911,7 +2039,11 @@ async function registerNetworkMocks(mocks) {
|
|
|
1911
2039
|
body = JSON.stringify(response.body_json);
|
|
1912
2040
|
contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
|
|
1913
2041
|
}
|
|
1914
|
-
|
|
2042
|
+
const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock);
|
|
2043
|
+
const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
|
|
2044
|
+
const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
|
|
2045
|
+
const status = response.status || mock.status || 200;
|
|
2046
|
+
const event = {
|
|
1915
2047
|
ok: true,
|
|
1916
2048
|
label: mock.label,
|
|
1917
2049
|
response_label: response.label || null,
|
|
@@ -1921,10 +2053,17 @@ async function registerNetworkMocks(mocks) {
|
|
|
1921
2053
|
sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
|
|
1922
2054
|
url: request.url(),
|
|
1923
2055
|
method,
|
|
1924
|
-
status
|
|
1925
|
-
}
|
|
2056
|
+
status,
|
|
2057
|
+
};
|
|
2058
|
+
if (shouldCaptureRequestBody) {
|
|
2059
|
+
event.request_body_matches = requestBodyFailures.length === 0;
|
|
2060
|
+
event.request_body_failures = requestBodyFailures;
|
|
2061
|
+
event.request_body_length = requestBody.length;
|
|
2062
|
+
event.request_body_sample = compactNetworkMockRequestBody(requestBody);
|
|
2063
|
+
}
|
|
2064
|
+
networkMockEvents.push(event);
|
|
1926
2065
|
await route.fulfill({
|
|
1927
|
-
status
|
|
2066
|
+
status,
|
|
1928
2067
|
headers,
|
|
1929
2068
|
contentType,
|
|
1930
2069
|
body,
|
package/dist/profile.d.cts
CHANGED
|
@@ -50,6 +50,11 @@ interface RiddleProofProfileNetworkMock extends RiddleProofProfileNetworkMockRes
|
|
|
50
50
|
repeat_responses?: boolean;
|
|
51
51
|
required_hit_count?: number;
|
|
52
52
|
required?: boolean;
|
|
53
|
+
capture_request_body?: boolean;
|
|
54
|
+
request_body_contains?: string[];
|
|
55
|
+
request_body_patterns?: string[];
|
|
56
|
+
request_body_not_contains?: string[];
|
|
57
|
+
request_body_not_patterns?: string[];
|
|
53
58
|
}
|
|
54
59
|
interface RiddleProofProfileRouteInventoryRoute {
|
|
55
60
|
name?: string;
|
package/dist/profile.d.ts
CHANGED
|
@@ -50,6 +50,11 @@ interface RiddleProofProfileNetworkMock extends RiddleProofProfileNetworkMockRes
|
|
|
50
50
|
repeat_responses?: boolean;
|
|
51
51
|
required_hit_count?: number;
|
|
52
52
|
required?: boolean;
|
|
53
|
+
capture_request_body?: boolean;
|
|
54
|
+
request_body_contains?: string[];
|
|
55
|
+
request_body_patterns?: string[];
|
|
56
|
+
request_body_not_contains?: string[];
|
|
57
|
+
request_body_not_patterns?: string[];
|
|
53
58
|
}
|
|
54
59
|
interface RiddleProofProfileRouteInventoryRoute {
|
|
55
60
|
name?: string;
|
package/dist/profile.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
resolveRiddleProofProfileTimeoutSec,
|
|
20
20
|
slugifyRiddleProofProfileName,
|
|
21
21
|
summarizeRiddleProofProfileResult
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-WSZ5AVKX.js";
|
|
23
23
|
export {
|
|
24
24
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
25
25
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|
|
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
292
292
|
blocking?: boolean;
|
|
293
293
|
details?: Record<string, unknown>;
|
|
294
294
|
ok: boolean;
|
|
295
|
-
action: "
|
|
295
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
296
296
|
state_path: string;
|
|
297
297
|
stage: any;
|
|
298
298
|
summary: string;
|
|
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
382
382
|
continueWithStage?: WorkflowStage | null;
|
|
383
383
|
blocking?: boolean;
|
|
384
384
|
details?: Record<string, unknown>;
|
|
385
|
-
action: "
|
|
385
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
386
386
|
state_path: string;
|
|
387
387
|
stage: any;
|
|
388
388
|
checkpoint: string;
|
|
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
659
659
|
error?: undefined;
|
|
660
660
|
} | {
|
|
661
661
|
ok: boolean;
|
|
662
|
-
action: "
|
|
662
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
663
663
|
state_path: string;
|
|
664
664
|
stage: any;
|
|
665
665
|
summary: string;
|
|
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
292
292
|
blocking?: boolean;
|
|
293
293
|
details?: Record<string, unknown>;
|
|
294
294
|
ok: boolean;
|
|
295
|
-
action: "
|
|
295
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
296
296
|
state_path: string;
|
|
297
297
|
stage: any;
|
|
298
298
|
summary: string;
|
|
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
382
382
|
continueWithStage?: WorkflowStage | null;
|
|
383
383
|
blocking?: boolean;
|
|
384
384
|
details?: Record<string, unknown>;
|
|
385
|
-
action: "
|
|
385
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
386
386
|
state_path: string;
|
|
387
387
|
stage: any;
|
|
388
388
|
checkpoint: string;
|
|
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
659
659
|
error?: undefined;
|
|
660
660
|
} | {
|
|
661
661
|
ok: boolean;
|
|
662
|
-
action: "
|
|
662
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
663
663
|
state_path: string;
|
|
664
664
|
stage: any;
|
|
665
665
|
summary: string;
|