@riddledc/riddle-proof 0.7.99 → 0.7.101

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.
@@ -807,6 +807,15 @@ function normalizeCheck(input, index) {
807
807
  input.body_contains ?? input.bodyContains ?? input.expected_body_contains ?? input.expectedBodyContains ?? input.response_body_contains ?? input.responseBodyContains ?? input.body_includes ?? input.bodyIncludes,
808
808
  `checks[${index}] body_contains`
809
809
  ) : void 0;
810
+ const bodyNotContains = isHttpStatusCheck ? normalizeStringList(
811
+ input.body_not_contains ?? input.bodyNotContains ?? input.expected_body_not_contains ?? input.expectedBodyNotContains ?? input.response_body_not_contains ?? input.responseBodyNotContains ?? input.body_absent ?? input.bodyAbsent,
812
+ `checks[${index}] body_not_contains`
813
+ ) : void 0;
814
+ const bodyNotPatterns = isHttpStatusCheck ? normalizeStringList(
815
+ input.body_not_patterns ?? input.bodyNotPatterns ?? input.expected_body_not_patterns ?? input.expectedBodyNotPatterns ?? input.response_body_not_patterns ?? input.responseBodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
816
+ `checks[${index}] body_not_patterns`
817
+ ) : void 0;
818
+ if (bodyNotPatterns?.length) validateRegexPatterns(bodyNotPatterns, `checks[${index}] body_not_patterns`);
810
819
  if (isLinkStatusCheck) {
811
820
  if (minCount !== void 0 && (!Number.isInteger(minCount) || minCount < 0)) {
812
821
  throw new Error(`checks[${index}] ${type} min_count must be a non-negative integer.`);
@@ -830,6 +839,8 @@ function normalizeCheck(input, index) {
830
839
  body: isHttpStatusCheck ? stringValue(input.body) : void 0,
831
840
  body_json: isHttpStatusCheck && hasBodyJson ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
832
841
  body_contains: bodyContains,
842
+ body_not_contains: bodyNotContains,
843
+ body_not_patterns: bodyNotPatterns,
833
844
  expected_texts: expectedTexts,
834
845
  link_selector: stringValue(input.link_selector) || stringValue(input.linkSelector),
835
846
  source_selector: stringValue(input.source_selector) || stringValue(input.sourceSelector),
@@ -1007,6 +1018,18 @@ function httpStatusBodyContainsFailures(result, check) {
1007
1018
  const observed = isRecord(result.body_contains) ? result.body_contains : {};
1008
1019
  return expected.filter((text) => observed[text] !== true);
1009
1020
  }
1021
+ function httpStatusBodyNotContainsFailures(result, check) {
1022
+ const forbidden = check.body_not_contains?.filter(Boolean) ?? [];
1023
+ if (!forbidden.length) return [];
1024
+ const observed = isRecord(result.body_not_contains) ? result.body_not_contains : {};
1025
+ return forbidden.filter((text) => observed[text] !== false);
1026
+ }
1027
+ function httpStatusBodyNotPatternFailures(result, check) {
1028
+ const forbidden = check.body_not_patterns?.filter(Boolean) ?? [];
1029
+ if (!forbidden.length) return [];
1030
+ const observed = isRecord(result.body_not_patterns) ? result.body_not_patterns : {};
1031
+ return forbidden.filter((pattern) => observed[pattern] !== false);
1032
+ }
1010
1033
  function linkStatusResultOk(result, check) {
1011
1034
  const status = numberValue(result.status);
1012
1035
  if (!httpStatusIsAllowed(status, check)) return false;
@@ -1022,6 +1045,8 @@ function linkStatusResultOk(result, check) {
1022
1045
  if (observedBytes === void 0 || observedBytes < check.min_bytes) return false;
1023
1046
  }
1024
1047
  if (httpStatusBodyContainsFailures(result, check).length) return false;
1048
+ if (httpStatusBodyNotContainsFailures(result, check).length) return false;
1049
+ if (httpStatusBodyNotPatternFailures(result, check).length) return false;
1025
1050
  return true;
1026
1051
  }
1027
1052
  function httpStatusEvidenceForCheck(viewport, check) {
@@ -1044,6 +1069,8 @@ function summarizeHttpStatusEvidence(viewport, check) {
1044
1069
  }
1045
1070
  const failures = [];
1046
1071
  const bodyContainsMissing = httpStatusBodyContainsFailures(statusEvidence, check);
1072
+ const bodyNotContainsFound = httpStatusBodyNotContainsFailures(statusEvidence, check);
1073
+ const bodyNotPatternsFound = httpStatusBodyNotPatternFailures(statusEvidence, check);
1047
1074
  if (!linkStatusResultOk(statusEvidence, check)) {
1048
1075
  failures.push({
1049
1076
  code: "http_status_failed",
@@ -1058,6 +1085,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
1058
1085
  allowed_content_types: check.allowed_content_types ?? null,
1059
1086
  body_contains: check.body_contains ?? null,
1060
1087
  body_contains_missing: bodyContainsMissing,
1088
+ body_not_contains: check.body_not_contains ?? null,
1089
+ body_not_contains_found: bodyNotContainsFound,
1090
+ body_not_patterns: check.body_not_patterns ?? null,
1091
+ body_not_patterns_found: bodyNotPatternsFound,
1061
1092
  body_sample: stringValue(statusEvidence.body_sample) ?? null
1062
1093
  });
1063
1094
  }
@@ -1075,6 +1106,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
1075
1106
  bytes: linkStatusObservedBytes(statusEvidence) ?? null,
1076
1107
  body_contains: isRecord(statusEvidence.body_contains) ? toJsonValue(statusEvidence.body_contains) : null,
1077
1108
  body_contains_missing: bodyContainsMissing,
1109
+ body_not_contains: isRecord(statusEvidence.body_not_contains) ? toJsonValue(statusEvidence.body_not_contains) : null,
1110
+ body_not_contains_found: bodyNotContainsFound,
1111
+ body_not_patterns: isRecord(statusEvidence.body_not_patterns) ? toJsonValue(statusEvidence.body_not_patterns) : null,
1112
+ body_not_patterns_found: bodyNotPatternsFound,
1078
1113
  body_sample: stringValue(statusEvidence.body_sample) ?? null,
1079
1114
  failures
1080
1115
  };
@@ -1686,6 +1721,8 @@ function assessCheckFromEvidence(check, evidence) {
1686
1721
  min_bytes: check.min_bytes ?? null,
1687
1722
  allowed_content_types: check.allowed_content_types ?? null,
1688
1723
  body_contains: check.body_contains ?? [],
1724
+ body_not_contains: check.body_not_contains ?? [],
1725
+ body_not_patterns: check.body_not_patterns ?? [],
1689
1726
  viewports: summaries.map((summary) => toJsonValue(summary)),
1690
1727
  failures: failed.flatMap((summary) => Array.isArray(summary.failures) ? summary.failures.map((failure) => toJsonValue({ viewport: stringValue(summary.viewport) ?? null, failure })) : [])
1691
1728
  },
@@ -2362,6 +2399,22 @@ function httpStatusBodyContainsFailures(result, check) {
2362
2399
  : {};
2363
2400
  return expected.filter((text) => observed[text] !== true);
2364
2401
  }
2402
+ function httpStatusBodyNotContainsFailures(result, check) {
2403
+ const forbidden = Array.isArray(check.body_not_contains) ? check.body_not_contains.filter(Boolean) : [];
2404
+ if (!forbidden.length) return [];
2405
+ const observed = result && typeof result.body_not_contains === "object" && !Array.isArray(result.body_not_contains)
2406
+ ? result.body_not_contains
2407
+ : {};
2408
+ return forbidden.filter((text) => observed[text] !== false);
2409
+ }
2410
+ function httpStatusBodyNotPatternFailures(result, check) {
2411
+ const forbidden = Array.isArray(check.body_not_patterns) ? check.body_not_patterns.filter(Boolean) : [];
2412
+ if (!forbidden.length) return [];
2413
+ const observed = result && typeof result.body_not_patterns === "object" && !Array.isArray(result.body_not_patterns)
2414
+ ? result.body_not_patterns
2415
+ : {};
2416
+ return forbidden.filter((pattern) => observed[pattern] !== false);
2417
+ }
2365
2418
  function linkStatusResultOk(result, check) {
2366
2419
  if (!result || typeof result !== "object" || Array.isArray(result)) return false;
2367
2420
  if (!httpStatusIsAllowed(result.status, check)) return false;
@@ -2377,6 +2430,8 @@ function linkStatusResultOk(result, check) {
2377
2430
  if (observedBytes === undefined || observedBytes < check.min_bytes) return false;
2378
2431
  }
2379
2432
  if (httpStatusBodyContainsFailures(result, check).length) return false;
2433
+ if (httpStatusBodyNotContainsFailures(result, check).length) return false;
2434
+ if (httpStatusBodyNotPatternFailures(result, check).length) return false;
2380
2435
  return true;
2381
2436
  }
2382
2437
  function summarizeHttpStatusEvidence(viewport, check) {
@@ -2395,6 +2450,8 @@ function summarizeHttpStatusEvidence(viewport, check) {
2395
2450
  }
2396
2451
  const failures = [];
2397
2452
  const bodyContainsMissing = httpStatusBodyContainsFailures(statusEvidence, check);
2453
+ const bodyNotContainsFound = httpStatusBodyNotContainsFailures(statusEvidence, check);
2454
+ const bodyNotPatternsFound = httpStatusBodyNotPatternFailures(statusEvidence, check);
2398
2455
  if (!linkStatusResultOk(statusEvidence, check)) {
2399
2456
  failures.push({
2400
2457
  code: "http_status_failed",
@@ -2409,6 +2466,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
2409
2466
  allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
2410
2467
  body_contains: Array.isArray(check.body_contains) ? check.body_contains : null,
2411
2468
  body_contains_missing: bodyContainsMissing,
2469
+ body_not_contains: Array.isArray(check.body_not_contains) ? check.body_not_contains : null,
2470
+ body_not_contains_found: bodyNotContainsFound,
2471
+ body_not_patterns: Array.isArray(check.body_not_patterns) ? check.body_not_patterns : null,
2472
+ body_not_patterns_found: bodyNotPatternsFound,
2412
2473
  body_sample: typeof statusEvidence.body_sample === "string" ? statusEvidence.body_sample : null,
2413
2474
  });
2414
2475
  }
@@ -2428,6 +2489,14 @@ function summarizeHttpStatusEvidence(viewport, check) {
2428
2489
  ? statusEvidence.body_contains
2429
2490
  : null,
2430
2491
  body_contains_missing: bodyContainsMissing,
2492
+ body_not_contains: statusEvidence.body_not_contains && typeof statusEvidence.body_not_contains === "object" && !Array.isArray(statusEvidence.body_not_contains)
2493
+ ? statusEvidence.body_not_contains
2494
+ : null,
2495
+ body_not_contains_found: bodyNotContainsFound,
2496
+ body_not_patterns: statusEvidence.body_not_patterns && typeof statusEvidence.body_not_patterns === "object" && !Array.isArray(statusEvidence.body_not_patterns)
2497
+ ? statusEvidence.body_not_patterns
2498
+ : null,
2499
+ body_not_patterns_found: bodyNotPatternsFound,
2431
2500
  body_sample: typeof statusEvidence.body_sample === "string" ? statusEvidence.body_sample : null,
2432
2501
  failures,
2433
2502
  };
@@ -4423,6 +4492,8 @@ async function collectHttpStatus(check) {
4423
4492
  body = check.body;
4424
4493
  }
4425
4494
  const bodyContains = Array.isArray(check.body_contains) ? check.body_contains.filter(Boolean) : [];
4495
+ const bodyNotContains = Array.isArray(check.body_not_contains) ? check.body_not_contains.filter(Boolean) : [];
4496
+ const bodyNotPatterns = Array.isArray(check.body_not_patterns) ? check.body_not_patterns.filter(Boolean) : [];
4426
4497
  const options = {
4427
4498
  method,
4428
4499
  redirect: "follow",
@@ -4448,15 +4519,17 @@ async function collectHttpStatus(check) {
4448
4519
  Object.assign(result, linkProbeResponseFields(response, method));
4449
4520
  result.url = url;
4450
4521
  result.status_text = response.statusText || "";
4451
- const shouldReadBody = check.require_nonzero_bytes === true || (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || bodyContains.length > 0;
4522
+ const shouldReadBody = check.require_nonzero_bytes === true || (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || bodyContains.length > 0 || bodyNotContains.length > 0 || bodyNotPatterns.length > 0;
4452
4523
  if (shouldReadBody) {
4453
4524
  try {
4454
4525
  const buffer = await response.arrayBuffer();
4455
4526
  result.bytes = buffer.byteLength;
4456
- if (bodyContains.length) {
4527
+ if (bodyContains.length || bodyNotContains.length || bodyNotPatterns.length) {
4457
4528
  const text = new TextDecoder().decode(buffer);
4458
4529
  result.body_sample = text.slice(0, 1000);
4459
- result.body_contains = Object.fromEntries(bodyContains.map((expected) => [expected, text.includes(expected)]));
4530
+ if (bodyContains.length) result.body_contains = Object.fromEntries(bodyContains.map((expected) => [expected, text.includes(expected)]));
4531
+ if (bodyNotContains.length) result.body_not_contains = Object.fromEntries(bodyNotContains.map((forbidden) => [forbidden, text.includes(forbidden)]));
4532
+ if (bodyNotPatterns.length) result.body_not_patterns = Object.fromEntries(bodyNotPatterns.map((pattern) => [pattern, new RegExp(pattern).test(text)]));
4460
4533
  }
4461
4534
  } catch (error) {
4462
4535
  result.error = String(error && error.message ? error.message : error).slice(0, 500);
@@ -4467,6 +4540,8 @@ async function collectHttpStatus(check) {
4467
4540
  && (check.require_nonzero_bytes !== true || ((linkProbeObservedBytes(result) || 0) > 0))
4468
4541
  && (!(typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || ((linkProbeObservedBytes(result) || 0) >= check.min_bytes))
4469
4542
  && (!bodyContains.length || bodyContains.every((expected) => result.body_contains && result.body_contains[expected] === true))
4543
+ && (!bodyNotContains.length || bodyNotContains.every((forbidden) => result.body_not_contains && result.body_not_contains[forbidden] === false))
4544
+ && (!bodyNotPatterns.length || bodyNotPatterns.every((pattern) => result.body_not_patterns && result.body_not_patterns[pattern] === false))
4470
4545
  && !result.error;
4471
4546
  return result;
4472
4547
  } catch (error) {
package/dist/cli.cjs CHANGED
@@ -7700,6 +7700,15 @@ function normalizeCheck(input, index) {
7700
7700
  input.body_contains ?? input.bodyContains ?? input.expected_body_contains ?? input.expectedBodyContains ?? input.response_body_contains ?? input.responseBodyContains ?? input.body_includes ?? input.bodyIncludes,
7701
7701
  `checks[${index}] body_contains`
7702
7702
  ) : void 0;
7703
+ const bodyNotContains = isHttpStatusCheck ? normalizeStringList(
7704
+ input.body_not_contains ?? input.bodyNotContains ?? input.expected_body_not_contains ?? input.expectedBodyNotContains ?? input.response_body_not_contains ?? input.responseBodyNotContains ?? input.body_absent ?? input.bodyAbsent,
7705
+ `checks[${index}] body_not_contains`
7706
+ ) : void 0;
7707
+ const bodyNotPatterns = isHttpStatusCheck ? normalizeStringList(
7708
+ input.body_not_patterns ?? input.bodyNotPatterns ?? input.expected_body_not_patterns ?? input.expectedBodyNotPatterns ?? input.response_body_not_patterns ?? input.responseBodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
7709
+ `checks[${index}] body_not_patterns`
7710
+ ) : void 0;
7711
+ if (bodyNotPatterns?.length) validateRegexPatterns(bodyNotPatterns, `checks[${index}] body_not_patterns`);
7703
7712
  if (isLinkStatusCheck) {
7704
7713
  if (minCount !== void 0 && (!Number.isInteger(minCount) || minCount < 0)) {
7705
7714
  throw new Error(`checks[${index}] ${type} min_count must be a non-negative integer.`);
@@ -7723,6 +7732,8 @@ function normalizeCheck(input, index) {
7723
7732
  body: isHttpStatusCheck ? stringValue2(input.body) : void 0,
7724
7733
  body_json: isHttpStatusCheck && hasBodyJson ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
7725
7734
  body_contains: bodyContains,
7735
+ body_not_contains: bodyNotContains,
7736
+ body_not_patterns: bodyNotPatterns,
7726
7737
  expected_texts: expectedTexts,
7727
7738
  link_selector: stringValue2(input.link_selector) || stringValue2(input.linkSelector),
7728
7739
  source_selector: stringValue2(input.source_selector) || stringValue2(input.sourceSelector),
@@ -7900,6 +7911,18 @@ function httpStatusBodyContainsFailures(result, check) {
7900
7911
  const observed = isRecord(result.body_contains) ? result.body_contains : {};
7901
7912
  return expected.filter((text) => observed[text] !== true);
7902
7913
  }
7914
+ function httpStatusBodyNotContainsFailures(result, check) {
7915
+ const forbidden = check.body_not_contains?.filter(Boolean) ?? [];
7916
+ if (!forbidden.length) return [];
7917
+ const observed = isRecord(result.body_not_contains) ? result.body_not_contains : {};
7918
+ return forbidden.filter((text) => observed[text] !== false);
7919
+ }
7920
+ function httpStatusBodyNotPatternFailures(result, check) {
7921
+ const forbidden = check.body_not_patterns?.filter(Boolean) ?? [];
7922
+ if (!forbidden.length) return [];
7923
+ const observed = isRecord(result.body_not_patterns) ? result.body_not_patterns : {};
7924
+ return forbidden.filter((pattern) => observed[pattern] !== false);
7925
+ }
7903
7926
  function linkStatusResultOk(result, check) {
7904
7927
  const status = numberValue(result.status);
7905
7928
  if (!httpStatusIsAllowed(status, check)) return false;
@@ -7915,6 +7938,8 @@ function linkStatusResultOk(result, check) {
7915
7938
  if (observedBytes === void 0 || observedBytes < check.min_bytes) return false;
7916
7939
  }
7917
7940
  if (httpStatusBodyContainsFailures(result, check).length) return false;
7941
+ if (httpStatusBodyNotContainsFailures(result, check).length) return false;
7942
+ if (httpStatusBodyNotPatternFailures(result, check).length) return false;
7918
7943
  return true;
7919
7944
  }
7920
7945
  function httpStatusEvidenceForCheck(viewport, check) {
@@ -7937,6 +7962,8 @@ function summarizeHttpStatusEvidence(viewport, check) {
7937
7962
  }
7938
7963
  const failures = [];
7939
7964
  const bodyContainsMissing = httpStatusBodyContainsFailures(statusEvidence, check);
7965
+ const bodyNotContainsFound = httpStatusBodyNotContainsFailures(statusEvidence, check);
7966
+ const bodyNotPatternsFound = httpStatusBodyNotPatternFailures(statusEvidence, check);
7940
7967
  if (!linkStatusResultOk(statusEvidence, check)) {
7941
7968
  failures.push({
7942
7969
  code: "http_status_failed",
@@ -7951,6 +7978,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
7951
7978
  allowed_content_types: check.allowed_content_types ?? null,
7952
7979
  body_contains: check.body_contains ?? null,
7953
7980
  body_contains_missing: bodyContainsMissing,
7981
+ body_not_contains: check.body_not_contains ?? null,
7982
+ body_not_contains_found: bodyNotContainsFound,
7983
+ body_not_patterns: check.body_not_patterns ?? null,
7984
+ body_not_patterns_found: bodyNotPatternsFound,
7954
7985
  body_sample: stringValue2(statusEvidence.body_sample) ?? null
7955
7986
  });
7956
7987
  }
@@ -7968,6 +7999,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
7968
7999
  bytes: linkStatusObservedBytes(statusEvidence) ?? null,
7969
8000
  body_contains: isRecord(statusEvidence.body_contains) ? toJsonValue(statusEvidence.body_contains) : null,
7970
8001
  body_contains_missing: bodyContainsMissing,
8002
+ body_not_contains: isRecord(statusEvidence.body_not_contains) ? toJsonValue(statusEvidence.body_not_contains) : null,
8003
+ body_not_contains_found: bodyNotContainsFound,
8004
+ body_not_patterns: isRecord(statusEvidence.body_not_patterns) ? toJsonValue(statusEvidence.body_not_patterns) : null,
8005
+ body_not_patterns_found: bodyNotPatternsFound,
7971
8006
  body_sample: stringValue2(statusEvidence.body_sample) ?? null,
7972
8007
  failures
7973
8008
  };
@@ -8579,6 +8614,8 @@ function assessCheckFromEvidence(check, evidence) {
8579
8614
  min_bytes: check.min_bytes ?? null,
8580
8615
  allowed_content_types: check.allowed_content_types ?? null,
8581
8616
  body_contains: check.body_contains ?? [],
8617
+ body_not_contains: check.body_not_contains ?? [],
8618
+ body_not_patterns: check.body_not_patterns ?? [],
8582
8619
  viewports: summaries.map((summary) => toJsonValue(summary)),
8583
8620
  failures: failed.flatMap((summary) => Array.isArray(summary.failures) ? summary.failures.map((failure) => toJsonValue({ viewport: stringValue2(summary.viewport) ?? null, failure })) : [])
8584
8621
  },
@@ -9239,6 +9276,22 @@ function httpStatusBodyContainsFailures(result, check) {
9239
9276
  : {};
9240
9277
  return expected.filter((text) => observed[text] !== true);
9241
9278
  }
9279
+ function httpStatusBodyNotContainsFailures(result, check) {
9280
+ const forbidden = Array.isArray(check.body_not_contains) ? check.body_not_contains.filter(Boolean) : [];
9281
+ if (!forbidden.length) return [];
9282
+ const observed = result && typeof result.body_not_contains === "object" && !Array.isArray(result.body_not_contains)
9283
+ ? result.body_not_contains
9284
+ : {};
9285
+ return forbidden.filter((text) => observed[text] !== false);
9286
+ }
9287
+ function httpStatusBodyNotPatternFailures(result, check) {
9288
+ const forbidden = Array.isArray(check.body_not_patterns) ? check.body_not_patterns.filter(Boolean) : [];
9289
+ if (!forbidden.length) return [];
9290
+ const observed = result && typeof result.body_not_patterns === "object" && !Array.isArray(result.body_not_patterns)
9291
+ ? result.body_not_patterns
9292
+ : {};
9293
+ return forbidden.filter((pattern) => observed[pattern] !== false);
9294
+ }
9242
9295
  function linkStatusResultOk(result, check) {
9243
9296
  if (!result || typeof result !== "object" || Array.isArray(result)) return false;
9244
9297
  if (!httpStatusIsAllowed(result.status, check)) return false;
@@ -9254,6 +9307,8 @@ function linkStatusResultOk(result, check) {
9254
9307
  if (observedBytes === undefined || observedBytes < check.min_bytes) return false;
9255
9308
  }
9256
9309
  if (httpStatusBodyContainsFailures(result, check).length) return false;
9310
+ if (httpStatusBodyNotContainsFailures(result, check).length) return false;
9311
+ if (httpStatusBodyNotPatternFailures(result, check).length) return false;
9257
9312
  return true;
9258
9313
  }
9259
9314
  function summarizeHttpStatusEvidence(viewport, check) {
@@ -9272,6 +9327,8 @@ function summarizeHttpStatusEvidence(viewport, check) {
9272
9327
  }
9273
9328
  const failures = [];
9274
9329
  const bodyContainsMissing = httpStatusBodyContainsFailures(statusEvidence, check);
9330
+ const bodyNotContainsFound = httpStatusBodyNotContainsFailures(statusEvidence, check);
9331
+ const bodyNotPatternsFound = httpStatusBodyNotPatternFailures(statusEvidence, check);
9275
9332
  if (!linkStatusResultOk(statusEvidence, check)) {
9276
9333
  failures.push({
9277
9334
  code: "http_status_failed",
@@ -9286,6 +9343,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
9286
9343
  allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
9287
9344
  body_contains: Array.isArray(check.body_contains) ? check.body_contains : null,
9288
9345
  body_contains_missing: bodyContainsMissing,
9346
+ body_not_contains: Array.isArray(check.body_not_contains) ? check.body_not_contains : null,
9347
+ body_not_contains_found: bodyNotContainsFound,
9348
+ body_not_patterns: Array.isArray(check.body_not_patterns) ? check.body_not_patterns : null,
9349
+ body_not_patterns_found: bodyNotPatternsFound,
9289
9350
  body_sample: typeof statusEvidence.body_sample === "string" ? statusEvidence.body_sample : null,
9290
9351
  });
9291
9352
  }
@@ -9305,6 +9366,14 @@ function summarizeHttpStatusEvidence(viewport, check) {
9305
9366
  ? statusEvidence.body_contains
9306
9367
  : null,
9307
9368
  body_contains_missing: bodyContainsMissing,
9369
+ body_not_contains: statusEvidence.body_not_contains && typeof statusEvidence.body_not_contains === "object" && !Array.isArray(statusEvidence.body_not_contains)
9370
+ ? statusEvidence.body_not_contains
9371
+ : null,
9372
+ body_not_contains_found: bodyNotContainsFound,
9373
+ body_not_patterns: statusEvidence.body_not_patterns && typeof statusEvidence.body_not_patterns === "object" && !Array.isArray(statusEvidence.body_not_patterns)
9374
+ ? statusEvidence.body_not_patterns
9375
+ : null,
9376
+ body_not_patterns_found: bodyNotPatternsFound,
9308
9377
  body_sample: typeof statusEvidence.body_sample === "string" ? statusEvidence.body_sample : null,
9309
9378
  failures,
9310
9379
  };
@@ -11300,6 +11369,8 @@ async function collectHttpStatus(check) {
11300
11369
  body = check.body;
11301
11370
  }
11302
11371
  const bodyContains = Array.isArray(check.body_contains) ? check.body_contains.filter(Boolean) : [];
11372
+ const bodyNotContains = Array.isArray(check.body_not_contains) ? check.body_not_contains.filter(Boolean) : [];
11373
+ const bodyNotPatterns = Array.isArray(check.body_not_patterns) ? check.body_not_patterns.filter(Boolean) : [];
11303
11374
  const options = {
11304
11375
  method,
11305
11376
  redirect: "follow",
@@ -11325,15 +11396,17 @@ async function collectHttpStatus(check) {
11325
11396
  Object.assign(result, linkProbeResponseFields(response, method));
11326
11397
  result.url = url;
11327
11398
  result.status_text = response.statusText || "";
11328
- const shouldReadBody = check.require_nonzero_bytes === true || (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || bodyContains.length > 0;
11399
+ const shouldReadBody = check.require_nonzero_bytes === true || (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || bodyContains.length > 0 || bodyNotContains.length > 0 || bodyNotPatterns.length > 0;
11329
11400
  if (shouldReadBody) {
11330
11401
  try {
11331
11402
  const buffer = await response.arrayBuffer();
11332
11403
  result.bytes = buffer.byteLength;
11333
- if (bodyContains.length) {
11404
+ if (bodyContains.length || bodyNotContains.length || bodyNotPatterns.length) {
11334
11405
  const text = new TextDecoder().decode(buffer);
11335
11406
  result.body_sample = text.slice(0, 1000);
11336
- result.body_contains = Object.fromEntries(bodyContains.map((expected) => [expected, text.includes(expected)]));
11407
+ if (bodyContains.length) result.body_contains = Object.fromEntries(bodyContains.map((expected) => [expected, text.includes(expected)]));
11408
+ if (bodyNotContains.length) result.body_not_contains = Object.fromEntries(bodyNotContains.map((forbidden) => [forbidden, text.includes(forbidden)]));
11409
+ if (bodyNotPatterns.length) result.body_not_patterns = Object.fromEntries(bodyNotPatterns.map((pattern) => [pattern, new RegExp(pattern).test(text)]));
11337
11410
  }
11338
11411
  } catch (error) {
11339
11412
  result.error = String(error && error.message ? error.message : error).slice(0, 500);
@@ -11344,6 +11417,8 @@ async function collectHttpStatus(check) {
11344
11417
  && (check.require_nonzero_bytes !== true || ((linkProbeObservedBytes(result) || 0) > 0))
11345
11418
  && (!(typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || ((linkProbeObservedBytes(result) || 0) >= check.min_bytes))
11346
11419
  && (!bodyContains.length || bodyContains.every((expected) => result.body_contains && result.body_contains[expected] === true))
11420
+ && (!bodyNotContains.length || bodyNotContains.every((forbidden) => result.body_not_contains && result.body_not_contains[forbidden] === false))
11421
+ && (!bodyNotPatterns.length || bodyNotPatterns.every((pattern) => result.body_not_patterns && result.body_not_patterns[pattern] === false))
11347
11422
  && !result.error;
11348
11423
  return result;
11349
11424
  } catch (error) {
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-W5PBTTMJ.js";
13
+ } from "./chunk-7FQU5UH7.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport
package/dist/index.cjs CHANGED
@@ -9536,6 +9536,15 @@ function normalizeCheck(input, index) {
9536
9536
  input.body_contains ?? input.bodyContains ?? input.expected_body_contains ?? input.expectedBodyContains ?? input.response_body_contains ?? input.responseBodyContains ?? input.body_includes ?? input.bodyIncludes,
9537
9537
  `checks[${index}] body_contains`
9538
9538
  ) : void 0;
9539
+ const bodyNotContains = isHttpStatusCheck ? normalizeStringList(
9540
+ input.body_not_contains ?? input.bodyNotContains ?? input.expected_body_not_contains ?? input.expectedBodyNotContains ?? input.response_body_not_contains ?? input.responseBodyNotContains ?? input.body_absent ?? input.bodyAbsent,
9541
+ `checks[${index}] body_not_contains`
9542
+ ) : void 0;
9543
+ const bodyNotPatterns = isHttpStatusCheck ? normalizeStringList(
9544
+ input.body_not_patterns ?? input.bodyNotPatterns ?? input.expected_body_not_patterns ?? input.expectedBodyNotPatterns ?? input.response_body_not_patterns ?? input.responseBodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
9545
+ `checks[${index}] body_not_patterns`
9546
+ ) : void 0;
9547
+ if (bodyNotPatterns?.length) validateRegexPatterns(bodyNotPatterns, `checks[${index}] body_not_patterns`);
9539
9548
  if (isLinkStatusCheck) {
9540
9549
  if (minCount !== void 0 && (!Number.isInteger(minCount) || minCount < 0)) {
9541
9550
  throw new Error(`checks[${index}] ${type} min_count must be a non-negative integer.`);
@@ -9559,6 +9568,8 @@ function normalizeCheck(input, index) {
9559
9568
  body: isHttpStatusCheck ? stringValue5(input.body) : void 0,
9560
9569
  body_json: isHttpStatusCheck && hasBodyJson ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
9561
9570
  body_contains: bodyContains,
9571
+ body_not_contains: bodyNotContains,
9572
+ body_not_patterns: bodyNotPatterns,
9562
9573
  expected_texts: expectedTexts,
9563
9574
  link_selector: stringValue5(input.link_selector) || stringValue5(input.linkSelector),
9564
9575
  source_selector: stringValue5(input.source_selector) || stringValue5(input.sourceSelector),
@@ -9736,6 +9747,18 @@ function httpStatusBodyContainsFailures(result, check) {
9736
9747
  const observed = isRecord2(result.body_contains) ? result.body_contains : {};
9737
9748
  return expected.filter((text) => observed[text] !== true);
9738
9749
  }
9750
+ function httpStatusBodyNotContainsFailures(result, check) {
9751
+ const forbidden = check.body_not_contains?.filter(Boolean) ?? [];
9752
+ if (!forbidden.length) return [];
9753
+ const observed = isRecord2(result.body_not_contains) ? result.body_not_contains : {};
9754
+ return forbidden.filter((text) => observed[text] !== false);
9755
+ }
9756
+ function httpStatusBodyNotPatternFailures(result, check) {
9757
+ const forbidden = check.body_not_patterns?.filter(Boolean) ?? [];
9758
+ if (!forbidden.length) return [];
9759
+ const observed = isRecord2(result.body_not_patterns) ? result.body_not_patterns : {};
9760
+ return forbidden.filter((pattern) => observed[pattern] !== false);
9761
+ }
9739
9762
  function linkStatusResultOk(result, check) {
9740
9763
  const status = numberValue3(result.status);
9741
9764
  if (!httpStatusIsAllowed(status, check)) return false;
@@ -9751,6 +9774,8 @@ function linkStatusResultOk(result, check) {
9751
9774
  if (observedBytes === void 0 || observedBytes < check.min_bytes) return false;
9752
9775
  }
9753
9776
  if (httpStatusBodyContainsFailures(result, check).length) return false;
9777
+ if (httpStatusBodyNotContainsFailures(result, check).length) return false;
9778
+ if (httpStatusBodyNotPatternFailures(result, check).length) return false;
9754
9779
  return true;
9755
9780
  }
9756
9781
  function httpStatusEvidenceForCheck(viewport, check) {
@@ -9773,6 +9798,8 @@ function summarizeHttpStatusEvidence(viewport, check) {
9773
9798
  }
9774
9799
  const failures = [];
9775
9800
  const bodyContainsMissing = httpStatusBodyContainsFailures(statusEvidence, check);
9801
+ const bodyNotContainsFound = httpStatusBodyNotContainsFailures(statusEvidence, check);
9802
+ const bodyNotPatternsFound = httpStatusBodyNotPatternFailures(statusEvidence, check);
9776
9803
  if (!linkStatusResultOk(statusEvidence, check)) {
9777
9804
  failures.push({
9778
9805
  code: "http_status_failed",
@@ -9787,6 +9814,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
9787
9814
  allowed_content_types: check.allowed_content_types ?? null,
9788
9815
  body_contains: check.body_contains ?? null,
9789
9816
  body_contains_missing: bodyContainsMissing,
9817
+ body_not_contains: check.body_not_contains ?? null,
9818
+ body_not_contains_found: bodyNotContainsFound,
9819
+ body_not_patterns: check.body_not_patterns ?? null,
9820
+ body_not_patterns_found: bodyNotPatternsFound,
9790
9821
  body_sample: stringValue5(statusEvidence.body_sample) ?? null
9791
9822
  });
9792
9823
  }
@@ -9804,6 +9835,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
9804
9835
  bytes: linkStatusObservedBytes(statusEvidence) ?? null,
9805
9836
  body_contains: isRecord2(statusEvidence.body_contains) ? toJsonValue(statusEvidence.body_contains) : null,
9806
9837
  body_contains_missing: bodyContainsMissing,
9838
+ body_not_contains: isRecord2(statusEvidence.body_not_contains) ? toJsonValue(statusEvidence.body_not_contains) : null,
9839
+ body_not_contains_found: bodyNotContainsFound,
9840
+ body_not_patterns: isRecord2(statusEvidence.body_not_patterns) ? toJsonValue(statusEvidence.body_not_patterns) : null,
9841
+ body_not_patterns_found: bodyNotPatternsFound,
9807
9842
  body_sample: stringValue5(statusEvidence.body_sample) ?? null,
9808
9843
  failures
9809
9844
  };
@@ -10415,6 +10450,8 @@ function assessCheckFromEvidence(check, evidence) {
10415
10450
  min_bytes: check.min_bytes ?? null,
10416
10451
  allowed_content_types: check.allowed_content_types ?? null,
10417
10452
  body_contains: check.body_contains ?? [],
10453
+ body_not_contains: check.body_not_contains ?? [],
10454
+ body_not_patterns: check.body_not_patterns ?? [],
10418
10455
  viewports: summaries.map((summary) => toJsonValue(summary)),
10419
10456
  failures: failed.flatMap((summary) => Array.isArray(summary.failures) ? summary.failures.map((failure) => toJsonValue({ viewport: stringValue5(summary.viewport) ?? null, failure })) : [])
10420
10457
  },
@@ -11091,6 +11128,22 @@ function httpStatusBodyContainsFailures(result, check) {
11091
11128
  : {};
11092
11129
  return expected.filter((text) => observed[text] !== true);
11093
11130
  }
11131
+ function httpStatusBodyNotContainsFailures(result, check) {
11132
+ const forbidden = Array.isArray(check.body_not_contains) ? check.body_not_contains.filter(Boolean) : [];
11133
+ if (!forbidden.length) return [];
11134
+ const observed = result && typeof result.body_not_contains === "object" && !Array.isArray(result.body_not_contains)
11135
+ ? result.body_not_contains
11136
+ : {};
11137
+ return forbidden.filter((text) => observed[text] !== false);
11138
+ }
11139
+ function httpStatusBodyNotPatternFailures(result, check) {
11140
+ const forbidden = Array.isArray(check.body_not_patterns) ? check.body_not_patterns.filter(Boolean) : [];
11141
+ if (!forbidden.length) return [];
11142
+ const observed = result && typeof result.body_not_patterns === "object" && !Array.isArray(result.body_not_patterns)
11143
+ ? result.body_not_patterns
11144
+ : {};
11145
+ return forbidden.filter((pattern) => observed[pattern] !== false);
11146
+ }
11094
11147
  function linkStatusResultOk(result, check) {
11095
11148
  if (!result || typeof result !== "object" || Array.isArray(result)) return false;
11096
11149
  if (!httpStatusIsAllowed(result.status, check)) return false;
@@ -11106,6 +11159,8 @@ function linkStatusResultOk(result, check) {
11106
11159
  if (observedBytes === undefined || observedBytes < check.min_bytes) return false;
11107
11160
  }
11108
11161
  if (httpStatusBodyContainsFailures(result, check).length) return false;
11162
+ if (httpStatusBodyNotContainsFailures(result, check).length) return false;
11163
+ if (httpStatusBodyNotPatternFailures(result, check).length) return false;
11109
11164
  return true;
11110
11165
  }
11111
11166
  function summarizeHttpStatusEvidence(viewport, check) {
@@ -11124,6 +11179,8 @@ function summarizeHttpStatusEvidence(viewport, check) {
11124
11179
  }
11125
11180
  const failures = [];
11126
11181
  const bodyContainsMissing = httpStatusBodyContainsFailures(statusEvidence, check);
11182
+ const bodyNotContainsFound = httpStatusBodyNotContainsFailures(statusEvidence, check);
11183
+ const bodyNotPatternsFound = httpStatusBodyNotPatternFailures(statusEvidence, check);
11127
11184
  if (!linkStatusResultOk(statusEvidence, check)) {
11128
11185
  failures.push({
11129
11186
  code: "http_status_failed",
@@ -11138,6 +11195,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
11138
11195
  allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
11139
11196
  body_contains: Array.isArray(check.body_contains) ? check.body_contains : null,
11140
11197
  body_contains_missing: bodyContainsMissing,
11198
+ body_not_contains: Array.isArray(check.body_not_contains) ? check.body_not_contains : null,
11199
+ body_not_contains_found: bodyNotContainsFound,
11200
+ body_not_patterns: Array.isArray(check.body_not_patterns) ? check.body_not_patterns : null,
11201
+ body_not_patterns_found: bodyNotPatternsFound,
11141
11202
  body_sample: typeof statusEvidence.body_sample === "string" ? statusEvidence.body_sample : null,
11142
11203
  });
11143
11204
  }
@@ -11157,6 +11218,14 @@ function summarizeHttpStatusEvidence(viewport, check) {
11157
11218
  ? statusEvidence.body_contains
11158
11219
  : null,
11159
11220
  body_contains_missing: bodyContainsMissing,
11221
+ body_not_contains: statusEvidence.body_not_contains && typeof statusEvidence.body_not_contains === "object" && !Array.isArray(statusEvidence.body_not_contains)
11222
+ ? statusEvidence.body_not_contains
11223
+ : null,
11224
+ body_not_contains_found: bodyNotContainsFound,
11225
+ body_not_patterns: statusEvidence.body_not_patterns && typeof statusEvidence.body_not_patterns === "object" && !Array.isArray(statusEvidence.body_not_patterns)
11226
+ ? statusEvidence.body_not_patterns
11227
+ : null,
11228
+ body_not_patterns_found: bodyNotPatternsFound,
11160
11229
  body_sample: typeof statusEvidence.body_sample === "string" ? statusEvidence.body_sample : null,
11161
11230
  failures,
11162
11231
  };
@@ -13152,6 +13221,8 @@ async function collectHttpStatus(check) {
13152
13221
  body = check.body;
13153
13222
  }
13154
13223
  const bodyContains = Array.isArray(check.body_contains) ? check.body_contains.filter(Boolean) : [];
13224
+ const bodyNotContains = Array.isArray(check.body_not_contains) ? check.body_not_contains.filter(Boolean) : [];
13225
+ const bodyNotPatterns = Array.isArray(check.body_not_patterns) ? check.body_not_patterns.filter(Boolean) : [];
13155
13226
  const options = {
13156
13227
  method,
13157
13228
  redirect: "follow",
@@ -13177,15 +13248,17 @@ async function collectHttpStatus(check) {
13177
13248
  Object.assign(result, linkProbeResponseFields(response, method));
13178
13249
  result.url = url;
13179
13250
  result.status_text = response.statusText || "";
13180
- const shouldReadBody = check.require_nonzero_bytes === true || (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || bodyContains.length > 0;
13251
+ const shouldReadBody = check.require_nonzero_bytes === true || (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || bodyContains.length > 0 || bodyNotContains.length > 0 || bodyNotPatterns.length > 0;
13181
13252
  if (shouldReadBody) {
13182
13253
  try {
13183
13254
  const buffer = await response.arrayBuffer();
13184
13255
  result.bytes = buffer.byteLength;
13185
- if (bodyContains.length) {
13256
+ if (bodyContains.length || bodyNotContains.length || bodyNotPatterns.length) {
13186
13257
  const text = new TextDecoder().decode(buffer);
13187
13258
  result.body_sample = text.slice(0, 1000);
13188
- result.body_contains = Object.fromEntries(bodyContains.map((expected) => [expected, text.includes(expected)]));
13259
+ if (bodyContains.length) result.body_contains = Object.fromEntries(bodyContains.map((expected) => [expected, text.includes(expected)]));
13260
+ if (bodyNotContains.length) result.body_not_contains = Object.fromEntries(bodyNotContains.map((forbidden) => [forbidden, text.includes(forbidden)]));
13261
+ if (bodyNotPatterns.length) result.body_not_patterns = Object.fromEntries(bodyNotPatterns.map((pattern) => [pattern, new RegExp(pattern).test(text)]));
13189
13262
  }
13190
13263
  } catch (error) {
13191
13264
  result.error = String(error && error.message ? error.message : error).slice(0, 500);
@@ -13196,6 +13269,8 @@ async function collectHttpStatus(check) {
13196
13269
  && (check.require_nonzero_bytes !== true || ((linkProbeObservedBytes(result) || 0) > 0))
13197
13270
  && (!(typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || ((linkProbeObservedBytes(result) || 0) >= check.min_bytes))
13198
13271
  && (!bodyContains.length || bodyContains.every((expected) => result.body_contains && result.body_contains[expected] === true))
13272
+ && (!bodyNotContains.length || bodyNotContains.every((forbidden) => result.body_not_contains && result.body_not_contains[forbidden] === false))
13273
+ && (!bodyNotPatterns.length || bodyNotPatterns.every((pattern) => result.body_not_patterns && result.body_not_patterns[pattern] === false))
13199
13274
  && !result.error;
13200
13275
  return result;
13201
13276
  } catch (error) {
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  resolveRiddleProofProfileTimeoutSec,
59
59
  slugifyRiddleProofProfileName,
60
60
  summarizeRiddleProofProfileResult
61
- } from "./chunk-W5PBTTMJ.js";
61
+ } from "./chunk-7FQU5UH7.js";
62
62
  import {
63
63
  DEFAULT_RIDDLE_API_BASE_URL,
64
64
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -850,6 +850,15 @@ function normalizeCheck(input, index) {
850
850
  input.body_contains ?? input.bodyContains ?? input.expected_body_contains ?? input.expectedBodyContains ?? input.response_body_contains ?? input.responseBodyContains ?? input.body_includes ?? input.bodyIncludes,
851
851
  `checks[${index}] body_contains`
852
852
  ) : void 0;
853
+ const bodyNotContains = isHttpStatusCheck ? normalizeStringList(
854
+ input.body_not_contains ?? input.bodyNotContains ?? input.expected_body_not_contains ?? input.expectedBodyNotContains ?? input.response_body_not_contains ?? input.responseBodyNotContains ?? input.body_absent ?? input.bodyAbsent,
855
+ `checks[${index}] body_not_contains`
856
+ ) : void 0;
857
+ const bodyNotPatterns = isHttpStatusCheck ? normalizeStringList(
858
+ input.body_not_patterns ?? input.bodyNotPatterns ?? input.expected_body_not_patterns ?? input.expectedBodyNotPatterns ?? input.response_body_not_patterns ?? input.responseBodyNotPatterns ?? input.body_forbidden_patterns ?? input.bodyForbiddenPatterns,
859
+ `checks[${index}] body_not_patterns`
860
+ ) : void 0;
861
+ if (bodyNotPatterns?.length) validateRegexPatterns(bodyNotPatterns, `checks[${index}] body_not_patterns`);
853
862
  if (isLinkStatusCheck) {
854
863
  if (minCount !== void 0 && (!Number.isInteger(minCount) || minCount < 0)) {
855
864
  throw new Error(`checks[${index}] ${type} min_count must be a non-negative integer.`);
@@ -873,6 +882,8 @@ function normalizeCheck(input, index) {
873
882
  body: isHttpStatusCheck ? stringValue(input.body) : void 0,
874
883
  body_json: isHttpStatusCheck && hasBodyJson ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
875
884
  body_contains: bodyContains,
885
+ body_not_contains: bodyNotContains,
886
+ body_not_patterns: bodyNotPatterns,
876
887
  expected_texts: expectedTexts,
877
888
  link_selector: stringValue(input.link_selector) || stringValue(input.linkSelector),
878
889
  source_selector: stringValue(input.source_selector) || stringValue(input.sourceSelector),
@@ -1050,6 +1061,18 @@ function httpStatusBodyContainsFailures(result, check) {
1050
1061
  const observed = isRecord(result.body_contains) ? result.body_contains : {};
1051
1062
  return expected.filter((text) => observed[text] !== true);
1052
1063
  }
1064
+ function httpStatusBodyNotContainsFailures(result, check) {
1065
+ const forbidden = check.body_not_contains?.filter(Boolean) ?? [];
1066
+ if (!forbidden.length) return [];
1067
+ const observed = isRecord(result.body_not_contains) ? result.body_not_contains : {};
1068
+ return forbidden.filter((text) => observed[text] !== false);
1069
+ }
1070
+ function httpStatusBodyNotPatternFailures(result, check) {
1071
+ const forbidden = check.body_not_patterns?.filter(Boolean) ?? [];
1072
+ if (!forbidden.length) return [];
1073
+ const observed = isRecord(result.body_not_patterns) ? result.body_not_patterns : {};
1074
+ return forbidden.filter((pattern) => observed[pattern] !== false);
1075
+ }
1053
1076
  function linkStatusResultOk(result, check) {
1054
1077
  const status = numberValue(result.status);
1055
1078
  if (!httpStatusIsAllowed(status, check)) return false;
@@ -1065,6 +1088,8 @@ function linkStatusResultOk(result, check) {
1065
1088
  if (observedBytes === void 0 || observedBytes < check.min_bytes) return false;
1066
1089
  }
1067
1090
  if (httpStatusBodyContainsFailures(result, check).length) return false;
1091
+ if (httpStatusBodyNotContainsFailures(result, check).length) return false;
1092
+ if (httpStatusBodyNotPatternFailures(result, check).length) return false;
1068
1093
  return true;
1069
1094
  }
1070
1095
  function httpStatusEvidenceForCheck(viewport, check) {
@@ -1087,6 +1112,8 @@ function summarizeHttpStatusEvidence(viewport, check) {
1087
1112
  }
1088
1113
  const failures = [];
1089
1114
  const bodyContainsMissing = httpStatusBodyContainsFailures(statusEvidence, check);
1115
+ const bodyNotContainsFound = httpStatusBodyNotContainsFailures(statusEvidence, check);
1116
+ const bodyNotPatternsFound = httpStatusBodyNotPatternFailures(statusEvidence, check);
1090
1117
  if (!linkStatusResultOk(statusEvidence, check)) {
1091
1118
  failures.push({
1092
1119
  code: "http_status_failed",
@@ -1101,6 +1128,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
1101
1128
  allowed_content_types: check.allowed_content_types ?? null,
1102
1129
  body_contains: check.body_contains ?? null,
1103
1130
  body_contains_missing: bodyContainsMissing,
1131
+ body_not_contains: check.body_not_contains ?? null,
1132
+ body_not_contains_found: bodyNotContainsFound,
1133
+ body_not_patterns: check.body_not_patterns ?? null,
1134
+ body_not_patterns_found: bodyNotPatternsFound,
1104
1135
  body_sample: stringValue(statusEvidence.body_sample) ?? null
1105
1136
  });
1106
1137
  }
@@ -1118,6 +1149,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
1118
1149
  bytes: linkStatusObservedBytes(statusEvidence) ?? null,
1119
1150
  body_contains: isRecord(statusEvidence.body_contains) ? toJsonValue(statusEvidence.body_contains) : null,
1120
1151
  body_contains_missing: bodyContainsMissing,
1152
+ body_not_contains: isRecord(statusEvidence.body_not_contains) ? toJsonValue(statusEvidence.body_not_contains) : null,
1153
+ body_not_contains_found: bodyNotContainsFound,
1154
+ body_not_patterns: isRecord(statusEvidence.body_not_patterns) ? toJsonValue(statusEvidence.body_not_patterns) : null,
1155
+ body_not_patterns_found: bodyNotPatternsFound,
1121
1156
  body_sample: stringValue(statusEvidence.body_sample) ?? null,
1122
1157
  failures
1123
1158
  };
@@ -1729,6 +1764,8 @@ function assessCheckFromEvidence(check, evidence) {
1729
1764
  min_bytes: check.min_bytes ?? null,
1730
1765
  allowed_content_types: check.allowed_content_types ?? null,
1731
1766
  body_contains: check.body_contains ?? [],
1767
+ body_not_contains: check.body_not_contains ?? [],
1768
+ body_not_patterns: check.body_not_patterns ?? [],
1732
1769
  viewports: summaries.map((summary) => toJsonValue(summary)),
1733
1770
  failures: failed.flatMap((summary) => Array.isArray(summary.failures) ? summary.failures.map((failure) => toJsonValue({ viewport: stringValue(summary.viewport) ?? null, failure })) : [])
1734
1771
  },
@@ -2405,6 +2442,22 @@ function httpStatusBodyContainsFailures(result, check) {
2405
2442
  : {};
2406
2443
  return expected.filter((text) => observed[text] !== true);
2407
2444
  }
2445
+ function httpStatusBodyNotContainsFailures(result, check) {
2446
+ const forbidden = Array.isArray(check.body_not_contains) ? check.body_not_contains.filter(Boolean) : [];
2447
+ if (!forbidden.length) return [];
2448
+ const observed = result && typeof result.body_not_contains === "object" && !Array.isArray(result.body_not_contains)
2449
+ ? result.body_not_contains
2450
+ : {};
2451
+ return forbidden.filter((text) => observed[text] !== false);
2452
+ }
2453
+ function httpStatusBodyNotPatternFailures(result, check) {
2454
+ const forbidden = Array.isArray(check.body_not_patterns) ? check.body_not_patterns.filter(Boolean) : [];
2455
+ if (!forbidden.length) return [];
2456
+ const observed = result && typeof result.body_not_patterns === "object" && !Array.isArray(result.body_not_patterns)
2457
+ ? result.body_not_patterns
2458
+ : {};
2459
+ return forbidden.filter((pattern) => observed[pattern] !== false);
2460
+ }
2408
2461
  function linkStatusResultOk(result, check) {
2409
2462
  if (!result || typeof result !== "object" || Array.isArray(result)) return false;
2410
2463
  if (!httpStatusIsAllowed(result.status, check)) return false;
@@ -2420,6 +2473,8 @@ function linkStatusResultOk(result, check) {
2420
2473
  if (observedBytes === undefined || observedBytes < check.min_bytes) return false;
2421
2474
  }
2422
2475
  if (httpStatusBodyContainsFailures(result, check).length) return false;
2476
+ if (httpStatusBodyNotContainsFailures(result, check).length) return false;
2477
+ if (httpStatusBodyNotPatternFailures(result, check).length) return false;
2423
2478
  return true;
2424
2479
  }
2425
2480
  function summarizeHttpStatusEvidence(viewport, check) {
@@ -2438,6 +2493,8 @@ function summarizeHttpStatusEvidence(viewport, check) {
2438
2493
  }
2439
2494
  const failures = [];
2440
2495
  const bodyContainsMissing = httpStatusBodyContainsFailures(statusEvidence, check);
2496
+ const bodyNotContainsFound = httpStatusBodyNotContainsFailures(statusEvidence, check);
2497
+ const bodyNotPatternsFound = httpStatusBodyNotPatternFailures(statusEvidence, check);
2441
2498
  if (!linkStatusResultOk(statusEvidence, check)) {
2442
2499
  failures.push({
2443
2500
  code: "http_status_failed",
@@ -2452,6 +2509,10 @@ function summarizeHttpStatusEvidence(viewport, check) {
2452
2509
  allowed_content_types: Array.isArray(check.allowed_content_types) ? check.allowed_content_types : null,
2453
2510
  body_contains: Array.isArray(check.body_contains) ? check.body_contains : null,
2454
2511
  body_contains_missing: bodyContainsMissing,
2512
+ body_not_contains: Array.isArray(check.body_not_contains) ? check.body_not_contains : null,
2513
+ body_not_contains_found: bodyNotContainsFound,
2514
+ body_not_patterns: Array.isArray(check.body_not_patterns) ? check.body_not_patterns : null,
2515
+ body_not_patterns_found: bodyNotPatternsFound,
2455
2516
  body_sample: typeof statusEvidence.body_sample === "string" ? statusEvidence.body_sample : null,
2456
2517
  });
2457
2518
  }
@@ -2471,6 +2532,14 @@ function summarizeHttpStatusEvidence(viewport, check) {
2471
2532
  ? statusEvidence.body_contains
2472
2533
  : null,
2473
2534
  body_contains_missing: bodyContainsMissing,
2535
+ body_not_contains: statusEvidence.body_not_contains && typeof statusEvidence.body_not_contains === "object" && !Array.isArray(statusEvidence.body_not_contains)
2536
+ ? statusEvidence.body_not_contains
2537
+ : null,
2538
+ body_not_contains_found: bodyNotContainsFound,
2539
+ body_not_patterns: statusEvidence.body_not_patterns && typeof statusEvidence.body_not_patterns === "object" && !Array.isArray(statusEvidence.body_not_patterns)
2540
+ ? statusEvidence.body_not_patterns
2541
+ : null,
2542
+ body_not_patterns_found: bodyNotPatternsFound,
2474
2543
  body_sample: typeof statusEvidence.body_sample === "string" ? statusEvidence.body_sample : null,
2475
2544
  failures,
2476
2545
  };
@@ -4466,6 +4535,8 @@ async function collectHttpStatus(check) {
4466
4535
  body = check.body;
4467
4536
  }
4468
4537
  const bodyContains = Array.isArray(check.body_contains) ? check.body_contains.filter(Boolean) : [];
4538
+ const bodyNotContains = Array.isArray(check.body_not_contains) ? check.body_not_contains.filter(Boolean) : [];
4539
+ const bodyNotPatterns = Array.isArray(check.body_not_patterns) ? check.body_not_patterns.filter(Boolean) : [];
4469
4540
  const options = {
4470
4541
  method,
4471
4542
  redirect: "follow",
@@ -4491,15 +4562,17 @@ async function collectHttpStatus(check) {
4491
4562
  Object.assign(result, linkProbeResponseFields(response, method));
4492
4563
  result.url = url;
4493
4564
  result.status_text = response.statusText || "";
4494
- const shouldReadBody = check.require_nonzero_bytes === true || (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || bodyContains.length > 0;
4565
+ const shouldReadBody = check.require_nonzero_bytes === true || (typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || bodyContains.length > 0 || bodyNotContains.length > 0 || bodyNotPatterns.length > 0;
4495
4566
  if (shouldReadBody) {
4496
4567
  try {
4497
4568
  const buffer = await response.arrayBuffer();
4498
4569
  result.bytes = buffer.byteLength;
4499
- if (bodyContains.length) {
4570
+ if (bodyContains.length || bodyNotContains.length || bodyNotPatterns.length) {
4500
4571
  const text = new TextDecoder().decode(buffer);
4501
4572
  result.body_sample = text.slice(0, 1000);
4502
- result.body_contains = Object.fromEntries(bodyContains.map((expected) => [expected, text.includes(expected)]));
4573
+ if (bodyContains.length) result.body_contains = Object.fromEntries(bodyContains.map((expected) => [expected, text.includes(expected)]));
4574
+ if (bodyNotContains.length) result.body_not_contains = Object.fromEntries(bodyNotContains.map((forbidden) => [forbidden, text.includes(forbidden)]));
4575
+ if (bodyNotPatterns.length) result.body_not_patterns = Object.fromEntries(bodyNotPatterns.map((pattern) => [pattern, new RegExp(pattern).test(text)]));
4503
4576
  }
4504
4577
  } catch (error) {
4505
4578
  result.error = String(error && error.message ? error.message : error).slice(0, 500);
@@ -4510,6 +4583,8 @@ async function collectHttpStatus(check) {
4510
4583
  && (check.require_nonzero_bytes !== true || ((linkProbeObservedBytes(result) || 0) > 0))
4511
4584
  && (!(typeof check.min_bytes === "number" && Number.isFinite(check.min_bytes)) || ((linkProbeObservedBytes(result) || 0) >= check.min_bytes))
4512
4585
  && (!bodyContains.length || bodyContains.every((expected) => result.body_contains && result.body_contains[expected] === true))
4586
+ && (!bodyNotContains.length || bodyNotContains.every((forbidden) => result.body_not_contains && result.body_not_contains[forbidden] === false))
4587
+ && (!bodyNotPatterns.length || bodyNotPatterns.every((pattern) => result.body_not_patterns && result.body_not_patterns[pattern] === false))
4513
4588
  && !result.error;
4514
4589
  return result;
4515
4590
  } catch (error) {
@@ -120,6 +120,8 @@ interface RiddleProofProfileCheck {
120
120
  body?: string;
121
121
  body_json?: JsonValue;
122
122
  body_contains?: string[];
123
+ body_not_contains?: string[];
124
+ body_not_patterns?: string[];
123
125
  expected_texts?: string[];
124
126
  link_selector?: string;
125
127
  source_selector?: string;
package/dist/profile.d.ts CHANGED
@@ -120,6 +120,8 @@ interface RiddleProofProfileCheck {
120
120
  body?: string;
121
121
  body_json?: JsonValue;
122
122
  body_contains?: string[];
123
+ body_not_contains?: string[];
124
+ body_not_patterns?: string[];
123
125
  expected_texts?: string[];
124
126
  link_selector?: string;
125
127
  source_selector?: string;
package/dist/profile.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  resolveRiddleProofProfileTimeoutSec,
20
20
  slugifyRiddleProofProfileName,
21
21
  summarizeRiddleProofProfileResult
22
- } from "./chunk-W5PBTTMJ.js";
22
+ } from "./chunk-7FQU5UH7.js";
23
23
  export {
24
24
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
25
25
  RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.99",
3
+ "version": "0.7.101",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",