@ploaness/governance 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/dist/a11y-hit-target.d.ts +35 -0
  2. package/dist/a11y-hit-target.d.ts.map +1 -0
  3. package/dist/a11y-hit-target.js +82 -0
  4. package/dist/a11y-hit-target.js.map +1 -0
  5. package/dist/a11y-incomplete.d.ts +18 -0
  6. package/dist/a11y-incomplete.d.ts.map +1 -0
  7. package/dist/a11y-incomplete.js +65 -0
  8. package/dist/a11y-incomplete.js.map +1 -0
  9. package/dist/access-boundary.d.ts +62 -6
  10. package/dist/access-boundary.d.ts.map +1 -1
  11. package/dist/access-boundary.js +201 -6
  12. package/dist/access-boundary.js.map +1 -1
  13. package/dist/admin-view-coverage.d.ts +25 -0
  14. package/dist/admin-view-coverage.d.ts.map +1 -0
  15. package/dist/admin-view-coverage.js +74 -0
  16. package/dist/admin-view-coverage.js.map +1 -0
  17. package/dist/asset-policy.d.ts.map +1 -1
  18. package/dist/asset-policy.js +1 -23
  19. package/dist/asset-policy.js.map +1 -1
  20. package/dist/axe-coverage.d.ts +40 -0
  21. package/dist/axe-coverage.d.ts.map +1 -0
  22. package/dist/axe-coverage.js +62 -0
  23. package/dist/axe-coverage.js.map +1 -0
  24. package/dist/banned-typography.d.ts.map +1 -1
  25. package/dist/banned-typography.js +26 -0
  26. package/dist/banned-typography.js.map +1 -1
  27. package/dist/blocklist-entries.d.ts +36 -0
  28. package/dist/blocklist-entries.d.ts.map +1 -0
  29. package/dist/blocklist-entries.js +338 -0
  30. package/dist/blocklist-entries.js.map +1 -0
  31. package/dist/blocklist.d.ts +91 -0
  32. package/dist/blocklist.d.ts.map +1 -0
  33. package/dist/blocklist.js +187 -0
  34. package/dist/blocklist.js.map +1 -0
  35. package/dist/build-diagnosis.d.ts +10 -0
  36. package/dist/build-diagnosis.d.ts.map +1 -0
  37. package/dist/build-diagnosis.js +38 -0
  38. package/dist/build-diagnosis.js.map +1 -0
  39. package/dist/container-failure.d.ts.map +1 -1
  40. package/dist/container-failure.js +1 -18
  41. package/dist/container-failure.js.map +1 -1
  42. package/dist/container-files.d.ts +16 -0
  43. package/dist/container-files.d.ts.map +1 -1
  44. package/dist/container-files.js +23 -8
  45. package/dist/container-files.js.map +1 -1
  46. package/dist/container-images.d.ts +43 -0
  47. package/dist/container-images.d.ts.map +1 -0
  48. package/dist/container-images.js +157 -0
  49. package/dist/container-images.js.map +1 -0
  50. package/dist/dependency-freshness.d.ts +8 -0
  51. package/dist/dependency-freshness.d.ts.map +1 -1
  52. package/dist/dependency-freshness.js +2 -18
  53. package/dist/dependency-freshness.js.map +1 -1
  54. package/dist/doc-blocks.d.ts.map +1 -1
  55. package/dist/doc-blocks.js +1 -13
  56. package/dist/doc-blocks.js.map +1 -1
  57. package/dist/document-references.d.ts.map +1 -1
  58. package/dist/document-references.js +7 -1
  59. package/dist/document-references.js.map +1 -1
  60. package/dist/editorconfig-policy.d.ts +7 -0
  61. package/dist/editorconfig-policy.d.ts.map +1 -1
  62. package/dist/editorconfig-policy.js +19 -12
  63. package/dist/editorconfig-policy.js.map +1 -1
  64. package/dist/environment-coherence.d.ts +87 -0
  65. package/dist/environment-coherence.d.ts.map +1 -0
  66. package/dist/environment-coherence.js +128 -0
  67. package/dist/environment-coherence.js.map +1 -0
  68. package/dist/environment-files.d.ts.map +1 -1
  69. package/dist/environment-files.js +1 -19
  70. package/dist/environment-files.js.map +1 -1
  71. package/dist/file-roles.d.ts +7 -2
  72. package/dist/file-roles.d.ts.map +1 -1
  73. package/dist/file-roles.js +10 -9
  74. package/dist/file-roles.js.map +1 -1
  75. package/dist/freshness-repair.d.ts +82 -0
  76. package/dist/freshness-repair.d.ts.map +1 -0
  77. package/dist/freshness-repair.js +91 -0
  78. package/dist/freshness-repair.js.map +1 -0
  79. package/dist/generated-denial.d.ts.map +1 -1
  80. package/dist/generated-denial.js +1 -9
  81. package/dist/generated-denial.js.map +1 -1
  82. package/dist/generated-drift.d.ts +18 -0
  83. package/dist/generated-drift.d.ts.map +1 -0
  84. package/dist/generated-drift.js +24 -0
  85. package/dist/generated-drift.js.map +1 -0
  86. package/dist/harness-package.d.ts +13 -0
  87. package/dist/harness-package.d.ts.map +1 -0
  88. package/dist/harness-package.js +20 -0
  89. package/dist/harness-package.js.map +1 -0
  90. package/dist/history-policy.d.ts.map +1 -1
  91. package/dist/history-policy.js +1 -7
  92. package/dist/history-policy.js.map +1 -1
  93. package/dist/image-assets.d.ts.map +1 -1
  94. package/dist/image-assets.js +1 -10
  95. package/dist/image-assets.js.map +1 -1
  96. package/dist/index.d.ts +22 -1
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +22 -1
  99. package/dist/index.js.map +1 -1
  100. package/dist/install-policy.d.ts +21 -0
  101. package/dist/install-policy.d.ts.map +1 -1
  102. package/dist/install-policy.js +53 -1
  103. package/dist/install-policy.js.map +1 -1
  104. package/dist/jsx-accessibility-scope.d.ts +17 -0
  105. package/dist/jsx-accessibility-scope.d.ts.map +1 -0
  106. package/dist/jsx-accessibility-scope.js +36 -0
  107. package/dist/jsx-accessibility-scope.js.map +1 -0
  108. package/dist/jsx-accessibility.d.ts +13 -0
  109. package/dist/jsx-accessibility.d.ts.map +1 -0
  110. package/dist/jsx-accessibility.js +188 -0
  111. package/dist/jsx-accessibility.js.map +1 -0
  112. package/dist/network-policy.d.ts.map +1 -1
  113. package/dist/network-policy.js +1 -12
  114. package/dist/network-policy.js.map +1 -1
  115. package/dist/oxlint-policy.d.ts +16 -0
  116. package/dist/oxlint-policy.d.ts.map +1 -0
  117. package/dist/oxlint-policy.js +28 -0
  118. package/dist/oxlint-policy.js.map +1 -0
  119. package/dist/oxlint-report.d.ts +29 -0
  120. package/dist/oxlint-report.d.ts.map +1 -0
  121. package/dist/oxlint-report.js +84 -0
  122. package/dist/oxlint-report.js.map +1 -0
  123. package/dist/oxlint-scope.d.ts +13 -0
  124. package/dist/oxlint-scope.d.ts.map +1 -0
  125. package/dist/oxlint-scope.js +46 -0
  126. package/dist/oxlint-scope.js.map +1 -0
  127. package/dist/oxlint-suppressions.d.ts +17 -0
  128. package/dist/oxlint-suppressions.d.ts.map +1 -0
  129. package/dist/oxlint-suppressions.js +72 -0
  130. package/dist/oxlint-suppressions.js.map +1 -0
  131. package/dist/paired-assets.d.ts.map +1 -1
  132. package/dist/paired-assets.js.map +1 -1
  133. package/dist/payload-access.d.ts +6 -0
  134. package/dist/payload-access.d.ts.map +1 -1
  135. package/dist/payload-access.js +188 -57
  136. package/dist/payload-access.js.map +1 -1
  137. package/dist/payload-configs.d.ts +23 -0
  138. package/dist/payload-configs.d.ts.map +1 -0
  139. package/dist/payload-configs.js +96 -0
  140. package/dist/payload-configs.js.map +1 -0
  141. package/dist/payload-defaults.d.ts +128 -0
  142. package/dist/payload-defaults.d.ts.map +1 -0
  143. package/dist/payload-defaults.js +295 -0
  144. package/dist/payload-defaults.js.map +1 -0
  145. package/dist/payload-field-access.d.ts +4 -0
  146. package/dist/payload-field-access.d.ts.map +1 -0
  147. package/dist/payload-field-access.js +51 -0
  148. package/dist/payload-field-access.js.map +1 -0
  149. package/dist/payload-policy.d.ts +13 -0
  150. package/dist/payload-policy.d.ts.map +1 -1
  151. package/dist/payload-policy.js +171 -32
  152. package/dist/payload-policy.js.map +1 -1
  153. package/dist/payload-source.js +2 -2
  154. package/dist/payload-source.js.map +1 -1
  155. package/dist/relationship-cleanup.d.ts +18 -0
  156. package/dist/relationship-cleanup.d.ts.map +1 -0
  157. package/dist/relationship-cleanup.js +96 -0
  158. package/dist/relationship-cleanup.js.map +1 -0
  159. package/dist/release-age.d.ts.map +1 -1
  160. package/dist/release-age.js +1 -16
  161. package/dist/release-age.js.map +1 -1
  162. package/dist/request-limits.d.ts.map +1 -1
  163. package/dist/request-limits.js +1 -11
  164. package/dist/request-limits.js.map +1 -1
  165. package/dist/route-coverage.d.ts +27 -0
  166. package/dist/route-coverage.d.ts.map +1 -0
  167. package/dist/route-coverage.js +57 -0
  168. package/dist/route-coverage.js.map +1 -0
  169. package/dist/route-map.d.ts +65 -0
  170. package/dist/route-map.d.ts.map +1 -0
  171. package/dist/route-map.js +168 -0
  172. package/dist/route-map.js.map +1 -0
  173. package/dist/run-plan.d.ts.map +1 -1
  174. package/dist/run-plan.js +1 -19
  175. package/dist/run-plan.js.map +1 -1
  176. package/dist/secret-policy.d.ts.map +1 -1
  177. package/dist/secret-policy.js +5 -15
  178. package/dist/secret-policy.js.map +1 -1
  179. package/dist/settings-layering.d.ts +8 -30
  180. package/dist/settings-layering.d.ts.map +1 -1
  181. package/dist/settings-layering.js +19 -48
  182. package/dist/settings-layering.js.map +1 -1
  183. package/dist/settings.d.ts +19 -2
  184. package/dist/settings.d.ts.map +1 -1
  185. package/dist/settings.js +5 -1
  186. package/dist/settings.js.map +1 -1
  187. package/dist/source-security.d.ts +4 -0
  188. package/dist/source-security.d.ts.map +1 -0
  189. package/dist/source-security.js +56 -0
  190. package/dist/source-security.js.map +1 -0
  191. package/dist/source-text.d.ts +16 -0
  192. package/dist/source-text.d.ts.map +1 -1
  193. package/dist/source-text.js +46 -16
  194. package/dist/source-text.js.map +1 -1
  195. package/dist/toolchain-pins.d.ts.map +1 -1
  196. package/dist/toolchain-pins.js +2 -19
  197. package/dist/toolchain-pins.js.map +1 -1
  198. package/dist/version-policy.d.ts +7 -8
  199. package/dist/version-policy.d.ts.map +1 -1
  200. package/dist/version-policy.js +46 -26
  201. package/dist/version-policy.js.map +1 -1
  202. package/dist/vulnerability-policy.d.ts.map +1 -1
  203. package/dist/vulnerability-policy.js +1 -11
  204. package/dist/vulnerability-policy.js.map +1 -1
  205. package/dist/wiring-policy.d.ts.map +1 -1
  206. package/dist/wiring-policy.js +4 -1
  207. package/dist/wiring-policy.js.map +1 -1
  208. package/dist/wiring-violation.d.ts.map +1 -1
  209. package/dist/wiring-violation.js +1 -6
  210. package/dist/wiring-violation.js.map +1 -1
  211. package/dist/workspace-policy.d.ts.map +1 -1
  212. package/dist/workspace-policy.js +3 -9
  213. package/dist/workspace-policy.js.map +1 -1
  214. package/dist/yaml-blocks.d.ts +10 -0
  215. package/dist/yaml-blocks.d.ts.map +1 -1
  216. package/dist/yaml-blocks.js +20 -0
  217. package/dist/yaml-blocks.js.map +1 -1
  218. package/package.json +2 -2
  219. package/dist/url-safety.d.ts +0 -12
  220. package/dist/url-safety.d.ts.map +0 -1
  221. package/dist/url-safety.js +0 -58
  222. package/dist/url-safety.js.map +0 -1
@@ -1,16 +1,7 @@
1
- // Payload-specific source policy: the rules that exist only because the project is a Payload CMS
2
- // application, and therefore the part of ploaness that a generic JavaScript harness cannot supply.
3
- //
4
- // ploaness governs a language and a framework; this module is the framework half. The rules here target
5
- // Payload defects that a type checker and a generic linter both miss: a Local API read that pulls an
6
- // unbounded relationship graph out of the database, and an access check that is deliberately bypassed.
7
- // The access-control rules - the ones about a decision left UNWRITTEN rather than written wrongly - are
8
- // in `payload-access.ts`, and the source reader both halves are decided by is in `payload-source.ts`.
9
- //
10
- // These rules were previously five GritQL files run as Biome plugins. Biome resolves a plugin path
11
- // relative to the config that declares it, which does not survive being extended from node_modules, so
12
- // they are reimplemented here instead. The move also makes them unit-testable, which they were not.
13
- import { findAnonymousDraftReads, findUndeclaredAccess, findUnhardenedAuth, findUnrestrictedUploads, } from './payload-access.js';
1
+ // Payload source policy for explicit read bounds, request threading, and access decisions.
2
+ import { findAnonymousDraftReads, findUndecidedSvgHeaders, findUndeclaredAccess, findUndeclaredVersionReads, findUnhardenedAuth, findUnlockableAuth, findUnrestrictedUploads, } from './payload-access.js';
3
+ import { findUnprotectedPrivilegedFields } from './payload-field-access.js';
4
+ import { findFailOpenSecretGuards } from './source-security.js';
14
5
  import { balancedArguments, lineOf, occurrences, stripComments, topLevelSlice, } from './source-text.js';
15
6
  // The Local API reads that must declare their bound. `find` returns a page, so either `depth` or `limit`
16
7
  // bounds it; the single-document and global reads return one document, so only `depth` applies.
@@ -34,26 +25,30 @@ const BOUNDED_CALLS = [
34
25
  reason: 'bound payload.findGlobal() with an explicit depth to cap relationship population',
35
26
  },
36
27
  ];
37
- // Only a call on an identifier that is recognisably the Payload instance is judged, so an unrelated
38
- // `array.find(...)` is never touched. `req` may itself be reached through a chain - `ctx.req.payload`
39
- // inside a hook is as much the Payload instance as `req.payload` - and excluding a preceding dot meant
40
- // every such call went unjudged. The chain is admitted only in front of `req`, so a property named
41
- // `payload` on some unrelated object is still left alone.
28
+ // Recognized Payload receivers exclude unrelated collection methods such as array.find().
42
29
  const PAYLOAD_RECEIVER = /(?:^|[^\w$.])(?:payload|(?:[\w$]+\.)*req\.payload|this\.payload)$/;
43
- const declaresKey = (topLevel, key) => new RegExp(String.raw `(^|[\s,])${key}\s*:`).test(topLevel);
44
- // An unterminated call, an options variable rather than a literal, or an object spread all make the
45
- // bound impossible to decide statically. Staying silent there keeps the gate free of false positives;
46
- // the reviewer still sees the call.
47
- const unboundedCallAt = (source, rule, found) => {
48
- if (!PAYLOAD_RECEIVER.test(source.slice(0, found))) {
30
+ // A property boundary keeps a value such as request: req from being read as a req property.
31
+ const declaresProperty = (topLevel, key) => new RegExp(String.raw `(?:^|,)\s*${key}\s*(?::|,|$)`).test(topLevel);
32
+ // The depth-one text of a call's options literal, or undefined when the call is not one to judge.
33
+ const topLevelOptionsAt = (source, call, found, receiver) => {
34
+ if (!receiver.test(source.slice(0, found))) {
49
35
  return undefined;
50
36
  }
51
- const argumentText = balancedArguments(source, found + rule.call.length - 1);
52
- if (argumentText === undefined || !argumentText.includes('{') || argumentText.includes('...')) {
53
- return undefined;
54
- }
55
- const topLevel = topLevelSlice(argumentText);
56
- if (rule.required.some((key) => declaresKey(topLevel, key))) {
37
+ const argumentText = balancedArguments(source, found + call.length - 1);
38
+ return argumentText?.trim().startsWith('{') === true ? topLevelSlice(argumentText) : undefined;
39
+ };
40
+ // A protected property must follow every spread that could replace it.
41
+ const hasEffectiveProperty = (topLevel, key) => {
42
+ const properties = [
43
+ ...topLevel.matchAll(new RegExp(String.raw `(?:^|,)\s*${key}\s*(?::|(?=,|$))`, 'gu')),
44
+ ];
45
+ const last = properties.at(-1);
46
+ return last !== undefined && last.index > topLevel.lastIndexOf('...');
47
+ };
48
+ const unboundedCallAt = (source, rule, found) => {
49
+ const topLevel = topLevelOptionsAt(source, rule.call, found, PAYLOAD_RECEIVER);
50
+ if (topLevel === undefined ||
51
+ rule.required.some((key) => hasEffectiveProperty(topLevel, key))) {
57
52
  return undefined;
58
53
  }
59
54
  return { line: lineOf(source, found), rule: rule.rule, reason: rule.reason };
@@ -61,12 +56,123 @@ const unboundedCallAt = (source, rule, found) => {
61
56
  const findUnboundedCalls = (source) => BOUNDED_CALLS.flatMap((rule) => occurrences(source, rule.call)
62
57
  .map((found) => unboundedCallAt(source, rule, found))
63
58
  .filter((violation) => violation !== undefined));
59
+ // The collection, global, and version operations whose options carry both the caller and the request.
60
+ // One catalogue feeds both rules below: adding a Payload operation to one contract and not the other
61
+ // would leave a call either outside its transaction or running as an administrator with a decorative
62
+ // user value.
63
+ const LOCAL_API_CALLS = [
64
+ '.count(',
65
+ '.create(',
66
+ '.delete(',
67
+ '.find(',
68
+ '.findByID(',
69
+ '.findDistinct(',
70
+ '.findGlobal(',
71
+ '.findGlobalVersionByID(',
72
+ '.findGlobalVersions(',
73
+ '.findVersionByID(',
74
+ '.findVersions(',
75
+ '.restoreGlobalVersion(',
76
+ '.restoreVersion(',
77
+ '.update(',
78
+ '.updateGlobal(',
79
+ ];
80
+ // Only the request-scoped instance is judged, which is the whole reason the rule can be trusted. A bare
81
+ // `payload` from `getPayload()` - a script, a seed, a Server Component that opened its own instance -
82
+ // has no request to thread, and demanding one would be asking for a value that does not exist. Reaching
83
+ // the instance THROUGH `req` is the proof that one does, so the omission is never a decision.
84
+ const REQUEST_RECEIVER = /(?:^|[^\w$.])(?:[\w$]+\.)*req\.payload$/;
85
+ const findOpaqueOptions = (source) => LOCAL_API_CALLS.flatMap((call) => occurrences(source, call).flatMap((found) => PAYLOAD_RECEIVER.test(source.slice(0, found)) &&
86
+ topLevelOptionsAt(source, call, found, PAYLOAD_RECEIVER) === undefined
87
+ ? [
88
+ {
89
+ line: lineOf(source, found),
90
+ rule: 'require-explicit-payload-options',
91
+ reason: `cannot check ${call.slice(1, -1)}() options; use an object literal and state ` +
92
+ 'the required depth/limit, req, and access settings after any spread',
93
+ },
94
+ ]
95
+ : []));
96
+ const unthreadedCallAt = (source, call, found) => {
97
+ const topLevel = topLevelOptionsAt(source, call, found, REQUEST_RECEIVER);
98
+ if (topLevel === undefined || hasEffectiveProperty(topLevel, 'req')) {
99
+ return undefined;
100
+ }
101
+ return {
102
+ line: lineOf(source, found),
103
+ rule: 'no-unthreaded-req',
104
+ reason: `pass req to ${call.slice(1, -1)}() so it joins the caller's transaction; reached through ` +
105
+ 'req.payload without it, the operation opens its own - it cannot see the in-flight write, and ' +
106
+ 'it is not rolled back with it',
107
+ };
108
+ };
109
+ // The defect this catches is invisible in a passing test suite and in a code review. A hook that reads
110
+ // or writes through `req.payload` without `req` looks identical to one that threads it, runs correctly
111
+ // whenever no transaction is open, and corrupts a document only when one is - which is precisely when a
112
+ // hook runs. Payload's own documentation calls threading it critical, and no type checker can require it
113
+ // because the parameter is optional for the callers that genuinely have no request.
114
+ const findUnthreadedRequests = (source) => LOCAL_API_CALLS.flatMap((call) => occurrences(source, call)
115
+ .map((found) => unthreadedCallAt(source, call, found))
116
+ .filter((violation) => violation !== undefined));
117
+ const OVERRIDE_PROPERTY = /(?:^|,)\s*overrideAccess\s*:\s*([^,]*)/g;
118
+ const overrideProperties = (topLevel) => [...topLevel.matchAll(OVERRIDE_PROPERTY)].map((match) => ({
119
+ index: match.index,
120
+ value: (match[1] ?? '').trim(),
121
+ }));
122
+ // A later spread can replace an earlier false. A false written after the last spread is the only form
123
+ // whose effective value this text reader can prove without resolving another object.
124
+ const hasEffectiveAccessControl = (topLevel) => {
125
+ const last = overrideProperties(topLevel).at(-1);
126
+ return last?.value === 'false' && last.index > topLevel.lastIndexOf('...');
127
+ };
128
+ const userAccessViolationAt = (source, call, found) => {
129
+ const topLevel = topLevelOptionsAt(source, call, found, PAYLOAD_RECEIVER);
130
+ if (topLevel === undefined ||
131
+ !(declaresProperty(topLevel, 'user') || topLevel.includes('...')) ||
132
+ hasEffectiveAccessControl(topLevel) ||
133
+ overrideProperties(topLevel).some((property) => property.value === 'true')) {
134
+ return undefined;
135
+ }
136
+ return {
137
+ line: lineOf(source, found),
138
+ rule: 'require-user-access-control',
139
+ reason: `set overrideAccess: false on ${call.slice(1, -1)}() after any spread; ` +
140
+ 'a supplied or inherited user otherwise does not enable access control. Payload ' +
141
+ 'runs the operation as an administrator and ignores that user for access control',
142
+ };
143
+ };
144
+ const findIgnoredUsers = (source) => LOCAL_API_CALLS.flatMap((call) => occurrences(source, call)
145
+ .map((found) => userAccessViolationAt(source, call, found))
146
+ .filter((violation) => violation !== undefined));
64
147
  const OVERRIDE_ACCESS = /overrideAccess\s*:\s*true/g;
65
148
  const findOverrideAccess = (source) => [...source.matchAll(OVERRIDE_ACCESS)].map((match) => ({
66
149
  line: lineOf(source, match.index),
67
150
  rule: 'no-override-access',
68
- reason: 'overrideAccess: true bypasses Payload access control; pass req so the access rules run, or drop the override',
151
+ reason: 'overrideAccess: true bypasses Payload access control; set overrideAccess: false so the ' +
152
+ 'access rules run against the calling user',
69
153
  }));
154
+ // The one layer whose caller is a stranger. Everywhere else a Local API call runs in a context the
155
+ // project already trusts - a hook inside the write that authored it, a scheduled job, a seed - and
156
+ // privilege there is legitimate rather than a mistake. A route handler serves whoever reached the URL,
157
+ // so what it is allowed to read is the one thing it must never inherit by saying nothing.
158
+ const ENDPOINT_PATH = /^src\/(?:endpoints\/|app\/(?:.*\/)?route\.[jt]s$)/u;
159
+ // A route must prove access enforcement rather than inherit Payload's privileged default.
160
+ const endpointAccessViolationAt = (source, call, found) => {
161
+ if (!PAYLOAD_RECEIVER.test(source.slice(0, found))) {
162
+ return undefined;
163
+ }
164
+ const topLevel = topLevelOptionsAt(source, call, found, PAYLOAD_RECEIVER);
165
+ if (topLevel !== undefined && hasEffectiveAccessControl(topLevel)) {
166
+ return undefined;
167
+ }
168
+ return {
169
+ line: lineOf(source, found),
170
+ rule: 'require-endpoint-access',
171
+ reason: `state overrideAccess: false on ${call.slice(1, -1)}() in a route handler; Payload defaults it to ` +
172
+ 'true, so an omission runs as an administrator and serves the document to whoever called the ' +
173
+ 'route - set overrideAccess: false',
174
+ };
175
+ };
70
176
  // Anchored on the keyword that immediately precedes the specifier rather than on the statement that
71
177
  // opens it. Requiring the whole statement to fit on one line missed a multi-line brace list, which is
72
178
  // how a long import is normally written, and requiring `from` missed both a side-effect import and the
@@ -102,7 +208,10 @@ const findDeepRelativeImports = (source) => [...source.matchAll(RELATIVE_IMPORT)
102
208
  * @param source the file's text.
103
209
  * @returns one violation per offending line.
104
210
  */
105
- export const findSourceViolations = (source) => findDeepRelativeImports(stripComments(source));
211
+ export const findSourceViolations = (source) => [
212
+ ...findDeepRelativeImports(stripComments(source)),
213
+ ...findFailOpenSecretGuards(source),
214
+ ];
106
215
  /**
107
216
  * The rules that are about Payload itself, which only a Payload package can break.
108
217
  * @param source the file's text.
@@ -113,12 +222,42 @@ export const findPayloadViolations = (source) => {
113
222
  // order to explain why the code avoids it must not be reported as that construct.
114
223
  const code = stripComments(source);
115
224
  return [
225
+ ...findOpaqueOptions(code),
116
226
  ...findUnboundedCalls(code),
227
+ ...findUnthreadedRequests(code),
228
+ ...findIgnoredUsers(code),
117
229
  ...findOverrideAccess(code),
118
230
  ...findUndeclaredAccess(code),
119
231
  ...findUnhardenedAuth(code),
232
+ ...findUnlockableAuth(code),
120
233
  ...findAnonymousDraftReads(code),
234
+ ...findUndeclaredVersionReads(code),
121
235
  ...findUnrestrictedUploads(code),
236
+ ...findUndecidedSvgHeaders(code),
237
+ ...findUnprotectedPrivilegedFields(code),
122
238
  ];
123
239
  };
240
+ /**
241
+ * The access decision a route handler has to state rather than inherit.
242
+ *
243
+ * The two rules that already read `overrideAccess` judge a value that is written down: one bans `true`,
244
+ * the other requires `false` beside a `user`. Neither can see an absent property, and absent is the
245
+ * dangerous spelling - Payload defaults `overrideAccess` to `true`, so the call that says nothing is
246
+ * the call that runs as an administrator. Under `src/endpoints` the omission is therefore the
247
+ * violation, which is why this rule reads the path and the others do not.
248
+ * @param filePath the repository-relative path of the file, which decides whether the rule applies.
249
+ * @param source the file's text.
250
+ * @returns one violation per Local API call that leaves the decision to the default.
251
+ */
252
+ export const findEndpointViolations = (filePath, source) => {
253
+ if (!ENDPOINT_PATH.test(filePath)) {
254
+ return [];
255
+ }
256
+ // Runs outside `findPayloadViolations`, so it strips its own comments: prose that names a call must
257
+ // not be read as one.
258
+ const code = stripComments(source);
259
+ return LOCAL_API_CALLS.flatMap((call) => occurrences(code, call)
260
+ .map((found) => endpointAccessViolationAt(code, call, found))
261
+ .filter((violation) => violation !== undefined));
262
+ };
124
263
  //# sourceMappingURL=payload-policy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"payload-policy.js","sourceRoot":"","sources":["../src/payload-policy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"payload-policy.js","sourceRoot":"","sources":["../src/payload-policy.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,+BAA+B,EAAE,MAAM,2BAA2B,CAAA;AAE3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AAC/D,OAAO,EACL,iBAAiB,EACjB,MAAM,EACN,WAAW,EACX,aAAa,EACb,aAAa,GACd,MAAM,kBAAkB,CAAA;AASzB,yGAAyG;AACzG,gGAAgG;AAChG,MAAM,aAAa,GAA+B;IAChD;QACE,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;QAC5B,IAAI,EAAE,mBAAmB;QACzB,MAAM,EACJ,uGAAuG;KAC1G;IACD;QACE,IAAI,EAAE,YAAY;QAClB,QAAQ,EAAE,CAAC,OAAO,CAAC;QACnB,IAAI,EAAE,uBAAuB;QAC7B,MAAM,EAAE,gFAAgF;KACzF;IACD;QACE,IAAI,EAAE,cAAc;QACpB,QAAQ,EAAE,CAAC,OAAO,CAAC;QACnB,IAAI,EAAE,yBAAyB;QAC/B,MAAM,EAAE,kFAAkF;KAC3F;CACF,CAAA;AAED,0FAA0F;AAC1F,MAAM,gBAAgB,GAAW,mEAAmE,CAAA;AAEpG,4FAA4F;AAC5F,MAAM,gBAAgB,GAAG,CAAC,QAAgB,EAAE,GAAW,EAAW,EAAE,CAClE,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAA,aAAa,GAAG,cAAc,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AAErE,kGAAkG;AAClG,MAAM,iBAAiB,GAAG,CACxB,MAAc,EACd,IAAY,EACZ,KAAa,EACb,QAAgB,EACI,EAAE;IACtB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QAC3C,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,YAAY,GAAuB,iBAAiB,CAAC,MAAM,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAC3F,OAAO,YAAY,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAChG,CAAC,CAAA;AAED,uEAAuE;AACvE,MAAM,oBAAoB,GAAG,CAAC,QAAgB,EAAE,GAAW,EAAW,EAAE;IACtE,MAAM,UAAU,GAA+B;QAC7C,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAA,aAAa,GAAG,kBAAkB,EAAE,IAAI,CAAC,CAAC;KACrF,CAAA;IACD,MAAM,IAAI,GAAgC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3D,OAAO,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;AACvE,CAAC,CAAA;AAED,MAAM,eAAe,GAAG,CACtB,MAAc,EACd,IAAqB,EACrB,KAAa,EACiB,EAAE;IAChC,MAAM,QAAQ,GAAuB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAA;IAClG,IACE,QAAQ,KAAK,SAAS;QACtB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAW,EAAW,EAAE,CAAC,oBAAoB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,EACjF,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAA;AAC9E,CAAC,CAAA;AAED,MAAM,kBAAkB,GAAG,CAAC,MAAc,EAA+B,EAAE,CACzE,aAAa,CAAC,OAAO,CAAC,CAAC,IAAqB,EAA+B,EAAE,CAC3E,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC;KAC3B,GAAG,CAAC,CAAC,KAAa,EAAgC,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;KAC1F,MAAM,CACL,CAAC,SAAuC,EAAiC,EAAE,CACzE,SAAS,KAAK,SAAS,CAC1B,CACJ,CAAA;AAEH,sGAAsG;AACtG,qGAAqG;AACrG,qGAAqG;AACrG,cAAc;AACd,MAAM,eAAe,GAAsB;IACzC,SAAS;IACT,UAAU;IACV,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,gBAAgB;IAChB,cAAc;IACd,yBAAyB;IACzB,sBAAsB;IACtB,mBAAmB;IACnB,gBAAgB;IAChB,wBAAwB;IACxB,kBAAkB;IAClB,UAAU;IACV,gBAAgB;CACjB,CAAA;AAED,wGAAwG;AACxG,sGAAsG;AACtG,wGAAwG;AACxG,8FAA8F;AAC9F,MAAM,gBAAgB,GAAW,yCAAyC,CAAA;AAE1E,MAAM,iBAAiB,GAAG,CAAC,MAAc,EAA+B,EAAE,CACxE,eAAe,CAAC,OAAO,CAAC,CAAC,IAAY,EAA+B,EAAE,CACpE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,KAAa,EAA+B,EAAE,CAC/E,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7C,iBAAiB,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,gBAAgB,CAAC,KAAK,SAAS;IACpE,CAAC,CAAC;QACE;YACE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;YAC3B,IAAI,EAAE,kCAAkC;YACxC,MAAM,EACJ,gBAAgB,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,8CAA8C;gBAC/E,qEAAqE;SACxE;KACF;IACH,CAAC,CAAC,EAAE,CACP,CACF,CAAA;AAEH,MAAM,gBAAgB,GAAG,CACvB,MAAc,EACd,IAAY,EACZ,KAAa,EACiB,EAAE;IAChC,MAAM,QAAQ,GAAuB,iBAAiB,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAA;IAC7F,IAAI,QAAQ,KAAK,SAAS,IAAI,oBAAoB,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC;QACpE,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;QAC3B,IAAI,EAAE,mBAAmB;QACzB,MAAM,EACJ,eAAe,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,2DAA2D;YAC3F,+FAA+F;YAC/F,+BAA+B;KAClC,CAAA;AACH,CAAC,CAAA;AAED,uGAAuG;AACvG,uGAAuG;AACvG,wGAAwG;AACxG,yGAAyG;AACzG,oFAAoF;AACpF,MAAM,sBAAsB,GAAG,CAAC,MAAc,EAA+B,EAAE,CAC7E,eAAe,CAAC,OAAO,CAAC,CAAC,IAAY,EAA+B,EAAE,CACpE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC;KACtB,GAAG,CAAC,CAAC,KAAa,EAAgC,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;KAC3F,MAAM,CACL,CAAC,SAAuC,EAAiC,EAAE,CACzE,SAAS,KAAK,SAAS,CAC1B,CACJ,CAAA;AAOH,MAAM,iBAAiB,GAAW,yCAAyC,CAAA;AAE3E,MAAM,kBAAkB,GAAG,CAAC,QAAgB,EAA+B,EAAE,CAC3E,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAC3C,CAAC,KAAsB,EAAoB,EAAE,CAAC,CAAC;IAC7C,KAAK,EAAE,KAAK,CAAC,KAAK;IAClB,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;CAC/B,CAAC,CACH,CAAA;AAEH,sGAAsG;AACtG,qFAAqF;AACrF,MAAM,yBAAyB,GAAG,CAAC,QAAgB,EAAW,EAAE;IAC9D,MAAM,IAAI,GAAiC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9E,OAAO,IAAI,EAAE,KAAK,KAAK,OAAO,IAAI,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;AAC5E,CAAC,CAAA;AAED,MAAM,qBAAqB,GAAG,CAC5B,MAAc,EACd,IAAY,EACZ,KAAa,EACiB,EAAE;IAChC,MAAM,QAAQ,GAAuB,iBAAiB,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAA;IAC7F,IACE,QAAQ,KAAK,SAAS;QACtB,CAAC,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjE,yBAAyB,CAAC,QAAQ,CAAC;QACnC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,IAAI,CAC/B,CAAC,QAA0B,EAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,KAAK,MAAM,CACnE,EACD,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;QAC3B,IAAI,EAAE,6BAA6B;QACnC,MAAM,EACJ,gCAAgC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,uBAAuB;YACxE,iFAAiF;YACjF,iFAAiF;KACpF,CAAA;AACH,CAAC,CAAA;AAED,MAAM,gBAAgB,GAAG,CAAC,MAAc,EAA+B,EAAE,CACvE,eAAe,CAAC,OAAO,CAAC,CAAC,IAAY,EAA+B,EAAE,CACpE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC;KACtB,GAAG,CAAC,CAAC,KAAa,EAAgC,EAAE,CACnD,qBAAqB,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAC3C;KACA,MAAM,CACL,CAAC,SAAuC,EAAiC,EAAE,CACzE,SAAS,KAAK,SAAS,CAC1B,CACJ,CAAA;AAEH,MAAM,eAAe,GAAW,4BAA4B,CAAA;AAE5D,MAAM,kBAAkB,GAAG,CAAC,MAAc,EAA+B,EAAE,CACzE,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CACvC,CAAC,KAAsB,EAAoB,EAAE,CAAC,CAAC;IAC7C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC;IACjC,IAAI,EAAE,oBAAoB;IAC1B,MAAM,EACJ,yFAAyF;QACzF,2CAA2C;CAC9C,CAAC,CACH,CAAA;AAEH,mGAAmG;AACnG,mGAAmG;AACnG,uGAAuG;AACvG,0FAA0F;AAC1F,MAAM,aAAa,GAAW,oDAAoD,CAAA;AAElF,0FAA0F;AAC1F,MAAM,yBAAyB,GAAG,CAChC,MAAc,EACd,IAAY,EACZ,KAAa,EACiB,EAAE;IAChC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QACnD,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,QAAQ,GAAuB,iBAAiB,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAA;IAC7F,IAAI,QAAQ,KAAK,SAAS,IAAI,yBAAyB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClE,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;QAC3B,IAAI,EAAE,yBAAyB;QAC/B,MAAM,EACJ,kCAAkC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gDAAgD;YACnG,8FAA8F;YAC9F,mCAAmC;KACtC,CAAA;AACH,CAAC,CAAA;AAED,oGAAoG;AACpG,sGAAsG;AACtG,uGAAuG;AACvG,iGAAiG;AACjG,MAAM,eAAe,GAAW,uDAAuD,CAAA;AACvF,oGAAoG;AACpG,iGAAiG;AACjG,EAAE;AACF,uGAAuG;AACvG,gGAAgG;AAChG,sGAAsG;AACtG,uGAAuG;AACvG,wGAAwG;AACxG,MAAM,sBAAsB,GAAW,2CAA2C,CAAA;AAElF,MAAM,uBAAuB,GAAG,CAAC,MAAc,EAA+B,EAAE,CAC9E,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAC3C,CAAC,KAAsB,EAA+B,EAAE;IACtD,MAAM,SAAS,GAAW,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IACxC,OAAO,sBAAsB,CAAC,IAAI,CAAC,SAAS,CAAC;QAC3C,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC;YACE;gBACE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC;gBACjC,IAAI,EAAE,0BAA0B;gBAChC,MAAM,EAAE,kEAAkE,SAAS,GAAG;aACvF;SACF,CAAA;AACP,CAAC,CACF,CAAA;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAAc,EAA+B,EAAE,CAAC;IACnF,GAAG,uBAAuB,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IACjD,GAAG,wBAAwB,CAAC,MAAM,CAAC;CACpC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAAc,EAA+B,EAAE;IACnF,mGAAmG;IACnG,kFAAkF;IAClF,MAAM,IAAI,GAAW,aAAa,CAAC,MAAM,CAAC,CAAA;IAC1C,OAAO;QACL,GAAG,iBAAiB,CAAC,IAAI,CAAC;QAC1B,GAAG,kBAAkB,CAAC,IAAI,CAAC;QAC3B,GAAG,sBAAsB,CAAC,IAAI,CAAC;QAC/B,GAAG,gBAAgB,CAAC,IAAI,CAAC;QACzB,GAAG,kBAAkB,CAAC,IAAI,CAAC;QAC3B,GAAG,oBAAoB,CAAC,IAAI,CAAC;QAC7B,GAAG,kBAAkB,CAAC,IAAI,CAAC;QAC3B,GAAG,kBAAkB,CAAC,IAAI,CAAC;QAC3B,GAAG,uBAAuB,CAAC,IAAI,CAAC;QAChC,GAAG,0BAA0B,CAAC,IAAI,CAAC;QACnC,GAAG,uBAAuB,CAAC,IAAI,CAAC;QAChC,GAAG,uBAAuB,CAAC,IAAI,CAAC;QAChC,GAAG,+BAA+B,CAAC,IAAI,CAAC;KACzC,CAAA;AACH,CAAC,CAAA;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACpC,QAAgB,EAChB,MAAc,EACe,EAAE;IAC/B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,CAAA;IACX,CAAC;IACD,oGAAoG;IACpG,sBAAsB;IACtB,MAAM,IAAI,GAAW,aAAa,CAAC,MAAM,CAAC,CAAA;IAC1C,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC,IAAY,EAA+B,EAAE,CAC3E,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC;SACpB,GAAG,CAAC,CAAC,KAAa,EAAgC,EAAE,CACnD,yBAAyB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAC7C;SACA,MAAM,CACL,CAAC,SAAuC,EAAiC,EAAE,CACzE,SAAS,KAAK,SAAS,CAC1B,CACJ,CAAA;AACH,CAAC,CAAA"}
@@ -41,8 +41,8 @@ export const depthOneValue = (wrapped, key) => {
41
41
  * @returns the block's depth-one keys, or an empty list when the key is absent.
42
42
  */
43
43
  export const depthOneBlockKeys = (wrapped, key) => {
44
- const at = depthOneKeyIndex(wrapped, key);
45
- return at === NOT_FOUND ? [] : topLevelKeys(wrapped, at);
44
+ const value = depthOneValue(wrapped, key);
45
+ return value?.trimStart().startsWith('{') === true ? topLevelKeys(value, 0) : [];
46
46
  };
47
47
  const withClose = (spans, close) => {
48
48
  const last = spans.at(LAST_CHARACTER);
@@ -1 +1 @@
1
- {"version":3,"file":"payload-source.js","sourceRoot":"","sources":["../src/payload-source.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"payload-source.js","sourceRoot":"","sources":["../src/payload-source.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { SpecSource } from './axe-coverage.js';
2
+ import { type PayloadViolation } from './payload-source.js';
3
+ /** A violation together with the file it was found in, because this rule reads more than one. */
4
+ export interface LocatedViolation {
5
+ readonly path: string;
6
+ readonly violation: PayloadViolation;
7
+ }
8
+ /**
9
+ * Every required single-value relationship whose target collection cannot be deleted because of it.
10
+ *
11
+ * Reads the whole candidate set at once, because the collection a `relationTo` names is declared in
12
+ * another file. A target this set does not declare - a collection a plugin contributes, say - is left
13
+ * alone rather than reported, since nothing here can see whether it takes its dependants down.
14
+ * @param files every source file the gate collected, as a path and its text.
15
+ * @returns one violation per unguarded relationship, located in the file that declares it.
16
+ */
17
+ export declare const findUnguardedRelationships: (files: readonly SpecSource[]) => readonly LocatedViolation[];
18
+ //# sourceMappingURL=relationship-cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationship-cleanup.d.ts","sourceRoot":"","sources":["../src/relationship-cleanup.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEnD,OAAO,EAAoC,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAG7F,iGAAiG;AACjG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAA;CACrC;AAkGD;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,GACrC,OAAO,SAAS,UAAU,EAAE,KAC3B,SAAS,gBAAgB,EA4B3B,CAAA"}
@@ -0,0 +1,96 @@
1
+ import { payloadConfigsIn } from './payload-configs.js';
2
+ import { depthOneBlockKeys, depthOneValue } from './payload-source.js';
3
+ import { enclosingLiteral, occurrences, stripComments } from './source-text.js';
4
+ // The two field types that put a foreign key on the table rather than in the join table. `upload` is
5
+ // one of them: it is a relationship to the media collection wearing a different name.
6
+ const KEYED_FIELD_TYPES = ['relationship', 'upload'];
7
+ // Biome normalises a project to single quotes; nothing obliges a project to have run it yet.
8
+ const QUOTE_STYLES = ["'", '"'];
9
+ const TRUE_VALUE = /^\s*true\b/;
10
+ const QUOTED_VALUE = /^\s*['"`]([^'"`]+)['"`]/;
11
+ const DRAFTS_ENABLED = /drafts\s*:\s*(?:true|\{)/;
12
+ const CLEANUP_HOOK = 'beforeDelete';
13
+ const declaresTrue = (field, key) => TRUE_VALUE.test(depthOneValue(field, key) ?? '');
14
+ // The slug a `relationTo` names, and only when it names exactly one. An array is the polymorphic form,
15
+ // which Payload stores in the join table instead and which therefore carries no column to constrain.
16
+ const singleTarget = (field) => QUOTED_VALUE.exec(depthOneValue(field, 'relationTo') ?? '')?.[1];
17
+ const slugOf = (body) => QUOTED_VALUE.exec(depthOneValue(body, 'slug') ?? '')?.[1];
18
+ // A collection with drafts enabled gets no null constraint anywhere, so none of its required fields can
19
+ // reach the contradiction.
20
+ const declaresDrafts = (body) => DRAFTS_ENABLED.test(depthOneValue(body, 'versions') ?? '');
21
+ // Which literal in a file IS a collection is `payload-configs.ts`'s question, and this rule asks it
22
+ // there rather than carrying a second copy of the answer. A global is dropped: it has no relationship
23
+ // column of its own to constrain. A collection with no readable slug is dropped too, because the whole
24
+ // rule is a lookup by slug.
25
+ //
26
+ // Comments are blanked before anything is read, the way every other Payload rule reads a file. The keys
27
+ // this rule looks for sit at depth one, and a depth-one key is recognised by the delimiter in front of
28
+ // it - so a comment between the previous entry and `hooks:` hid the hook, and the rule reported a
29
+ // collection that was guarded. `stripComments` preserves offsets and newlines, so the reported line is
30
+ // still the line of the source.
31
+ const collectionsIn = (file) => payloadConfigsIn(stripComments(file.source))
32
+ .filter((found) => found.kind.kind === 'collection')
33
+ .flatMap((found) => {
34
+ const slug = slugOf(found.body);
35
+ return slug === undefined
36
+ ? []
37
+ : [{ path: file.path, slug, body: found.body, line: found.line }];
38
+ });
39
+ // Every field of one collection that puts a NOT NULL column against another collection's primary key.
40
+ // The field literal is recovered from the offset of its own `type` key, so a relationship nested inside
41
+ // an array or a group is read exactly like one declared at the top: both get a column, on their own
42
+ // table, with the same pair of settings.
43
+ const constrainedTargets = (collection) => {
44
+ if (declaresDrafts(collection.body)) {
45
+ return [];
46
+ }
47
+ return KEYED_FIELD_TYPES.flatMap((fieldType) => QUOTE_STYLES.flatMap((quote) => occurrences(collection.body, `${quote}${fieldType}${quote}`))
48
+ .map((at) => {
49
+ const field = enclosingLiteral(collection.body, at);
50
+ return field === undefined || !isConstrained(field, fieldType)
51
+ ? undefined
52
+ : singleTarget(field);
53
+ })
54
+ .filter((slug) => slug !== undefined));
55
+ };
56
+ // The four conditions the adapter itself applies, read back off the field. `hasMany` moves the
57
+ // relationship into the join table; `admin.condition` and an array `relationTo` each drop the null
58
+ // constraint; and without `required` there is nothing to contradict.
59
+ const isConstrained = (field, fieldType) => QUOTED_VALUE.exec(depthOneValue(field, 'type') ?? '')?.[1] === fieldType &&
60
+ declaresTrue(field, 'required') &&
61
+ !declaresTrue(field, 'hasMany') &&
62
+ !depthOneBlockKeys(field, 'admin').includes('condition') &&
63
+ singleTarget(field) !== undefined;
64
+ const declaresCleanup = (target) => depthOneBlockKeys(target.body, 'hooks').includes(CLEANUP_HOOK);
65
+ const reason = (target) => `a required relationship to \`${target}\` gives this table a NOT NULL column against a foreign key ` +
66
+ `Payload declares ON DELETE SET NULL, so deleting a ${target} row aborts on this table's ` +
67
+ `constraint; declare a ${CLEANUP_HOOK} hook on ${target} that removes its dependants first`;
68
+ /**
69
+ * Every required single-value relationship whose target collection cannot be deleted because of it.
70
+ *
71
+ * Reads the whole candidate set at once, because the collection a `relationTo` names is declared in
72
+ * another file. A target this set does not declare - a collection a plugin contributes, say - is left
73
+ * alone rather than reported, since nothing here can see whether it takes its dependants down.
74
+ * @param files every source file the gate collected, as a path and its text.
75
+ * @returns one violation per unguarded relationship, located in the file that declares it.
76
+ */
77
+ export const findUnguardedRelationships = (files) => {
78
+ const collections = files.flatMap((file) => collectionsIn(file));
79
+ const bySlug = new Map(collections.map((collection) => [
80
+ collection.slug,
81
+ collection,
82
+ ]));
83
+ return collections.flatMap((collection) => constrainedTargets(collection)
84
+ .map((slug) => bySlug.get(slug))
85
+ .filter((target) => target !== undefined)
86
+ .filter((target) => !declaresCleanup(target))
87
+ .map((target) => ({
88
+ path: collection.path,
89
+ violation: {
90
+ line: collection.line,
91
+ rule: 'require-relationship-cleanup',
92
+ reason: reason(target.slug),
93
+ },
94
+ })));
95
+ };
96
+ //# sourceMappingURL=relationship-cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationship-cleanup.js","sourceRoot":"","sources":["../src/relationship-cleanup.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"release-age.d.ts","sourceRoot":"","sources":["../src/release-age.ts"],"names":[],"mappings":"AAmBA,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB,EAAE,MAAW,CAAA;AAEjD,wFAAwF;AACxF,MAAM,WAAW,UAAU;IACzB,kFAAkF;IAClF,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,sDAAsD;IACtD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,UAAU,KAAG,MAAM,GAAG,SAGW,CAAA;AAErE;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,KAAK,UAAU,KAAG,OAGpD,CAAA"}
1
+ {"version":3,"file":"release-age.d.ts","sourceRoot":"","sources":["../src/release-age.ts"],"names":[],"mappings":"AAIA,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB,EAAE,MAAW,CAAA;AAEjD,wFAAwF;AACxF,MAAM,WAAW,UAAU;IACzB,kFAAkF;IAClF,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,sDAAsD;IACtD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,UAAU,KAAG,MAAM,GAAG,SAGW,CAAA;AAErE;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,KAAK,UAAU,KAAG,OAGpD,CAAA"}
@@ -1,19 +1,4 @@
1
- // Whether a published release is old enough to be installed at all.
2
- //
3
- // pnpm refuses a release younger than a minimum age. That guards against a compromised publish: a
4
- // malicious version is usually pulled within hours of being noticed, so the wait costs a day and buys
5
- // the window in which the registry corrects itself. The freshness report has to know about it, because
6
- // an update named without it sends a reader to an install pnpm will refuse, and the reader then either
7
- // waits without knowing why or spends the guard on an exclusion to get past it.
8
- //
9
- // The floor is pnpm's rather than ploaness's, and pnpm neither documents the default nor answers
10
- // `pnpm config get minimumReleaseAge` for it. The value below was established by observation - refused
11
- // at 13h and again at 23h58m, taken at 33h and at six days - so it is stated as a named constant a
12
- // reader can check and a maintainer can correct, rather than buried inside a comparison.
13
- //
14
- // Held is a third thing the report can say, not a fourth verdict the gate can reach. Nothing here
15
- // fails a build: a release too young to install is a fact about the registry, never a defect in the
16
- // project reading it.
1
+ // Classify whether a published release satisfies the install-age floor.
17
2
  const MILLISECONDS_PER_HOUR = 3_600_000;
18
3
  /** Hours a published release must have existed before pnpm will install it. */
19
4
  export const RELEASE_AGE_FLOOR_HOURS = 24;
@@ -1 +1 @@
1
- {"version":3,"file":"release-age.js","sourceRoot":"","sources":["../src/release-age.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,kGAAkG;AAClG,sGAAsG;AACtG,uGAAuG;AACvG,uGAAuG;AACvG,gFAAgF;AAChF,EAAE;AACF,iGAAiG;AACjG,uGAAuG;AACvG,mGAAmG;AACnG,yFAAyF;AACzF,EAAE;AACF,kGAAkG;AAClG,oGAAoG;AACpG,sBAAsB;AAEtB,MAAM,qBAAqB,GAAW,SAAS,CAAA;AAE/C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,uBAAuB,GAAW,EAAE,CAAA;AAUjD;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAe,EAAsB,EAAE,CACpE,GAAG,CAAC,WAAW,KAAK,SAAS;IAC3B,CAAC,CAAC,SAAS;IACX,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,qBAAqB,CAAC,CAAA;AAErE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAe,EAAW,EAAE;IAC7D,MAAM,KAAK,GAAuB,cAAc,CAAC,GAAG,CAAC,CAAA;IACrD,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,GAAG,uBAAuB,CAAA;AAC/D,CAAC,CAAA"}
1
+ {"version":3,"file":"release-age.js","sourceRoot":"","sources":["../src/release-age.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAExE,MAAM,qBAAqB,GAAW,SAAS,CAAA;AAE/C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,uBAAuB,GAAW,EAAE,CAAA;AAUjD;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAe,EAAsB,EAAE,CACpE,GAAG,CAAC,WAAW,KAAK,SAAS;IAC3B,CAAC,CAAC,SAAS;IACX,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,qBAAqB,CAAC,CAAA;AAErE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAe,EAAW,EAAE;IAC7D,MAAM,KAAK,GAAuB,cAAc,CAAC,GAAG,CAAC,CAAA;IACrD,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,GAAG,uBAAuB,CAAA;AAC/D,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"request-limits.d.ts","sourceRoot":"","sources":["../src/request-limits.ts"],"names":[],"mappings":"AAYA;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAe,CAAA;AAEhD;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAgB,CAAA;AAExD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAU,CAAA;AAE7C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,GAAU,IAAI,EAAE,MAAM,EACnD,OAAO,SAAS,IAAI,EAAE,EACtB,OAAO,MAAM,EACb,MAAM,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,KACpC,OAAO,CAAC,SAAS,MAAM,EAAE,CAS3B,CAAA"}
1
+ {"version":3,"file":"request-limits.d.ts","sourceRoot":"","sources":["../src/request-limits.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAe,CAAA;AAEhD;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAgB,CAAA;AAExD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAU,CAAA;AAE7C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,GAAU,IAAI,EAAE,MAAM,EACnD,OAAO,SAAS,IAAI,EAAE,EACtB,OAAO,MAAM,EACb,MAAM,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,KACpC,OAAO,CAAC,SAAS,MAAM,EAAE,CAS3B,CAAA"}
@@ -1,14 +1,4 @@
1
- // The bounds the harness places on its OWN outbound work, and the one combinator that applies them.
2
- //
3
- // This is the first async module among the rules, and it is here rather than in the CLI for the reason
4
- // the split states: what it decides is a policy - how long the harness waits, and how many sockets it
5
- // will hold open at once - and a policy expressed as a pure function can be asserted against, while the
6
- // same policy expressed inline in a fetch call can only be observed by hanging. Nothing here performs
7
- // I/O; the work is supplied by the caller, which is where the I/O stays.
8
- //
9
- // The bound that was missing cost more than the bound that was wrong. An unbounded fetch produces no
10
- // output and no verdict, so on a team's CI a stuck gate is indistinguishable from a stuck build - and
11
- // unlike a gate failure, nothing says which it is, because nothing ends.
1
+ // Bound the harness outbound work so registry checks finish without unbounded concurrency.
12
2
  /**
13
3
  * How long any single request to a package or image registry may take.
14
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"request-limits.js","sourceRoot":"","sources":["../src/request-limits.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,EAAE;AACF,uGAAuG;AACvG,sGAAsG;AACtG,wGAAwG;AACxG,sGAAsG;AACtG,yEAAyE;AACzE,EAAE;AACF,qGAAqG;AACrG,sGAAsG;AACtG,yEAAyE;AAEzE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAW,MAAM,CAAA;AAEhD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAW,OAAO,CAAA;AAExD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAW,CAAC,CAAA;AAE7C;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,KAAsB,EACtB,KAAa,EACb,IAAqC,EACT,EAAE;IAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAA;IACX,CAAC;IACD,MAAM,KAAK,GAAW,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IACxC,MAAM,QAAQ,GAAsB,MAAM,OAAO,CAAC,GAAG,CACnD,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,IAAU,EAAmB,EAAE,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CACnF,CAAA;IACD,OAAO,CAAC,GAAG,QAAQ,EAAE,GAAG,CAAC,MAAM,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;AACtF,CAAC,CAAA"}
1
+ {"version":3,"file":"request-limits.js","sourceRoot":"","sources":["../src/request-limits.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAE3F;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAW,MAAM,CAAA;AAEhD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAW,OAAO,CAAA;AAExD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAW,CAAC,CAAA;AAE7C;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,KAAsB,EACtB,KAAa,EACb,IAAqC,EACT,EAAE;IAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAA;IACX,CAAC;IACD,MAAM,KAAK,GAAW,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IACxC,MAAM,QAAQ,GAAsB,MAAM,OAAO,CAAC,GAAG,CACnD,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,IAAU,EAAmB,EAAE,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CACnF,CAAA;IACD,OAAO,CAAC,GAAG,QAAQ,EAAE,GAAG,CAAC,MAAM,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;AACtF,CAAC,CAAA"}
@@ -0,0 +1,27 @@
1
+ import { type SpecSource } from './axe-coverage.js';
2
+ import { type DeclaredRoute } from './route-map.js';
3
+ /** Everything the coverage question is answered from. */
4
+ export interface RouteSweep {
5
+ readonly declaredRoutes: readonly DeclaredRoute[];
6
+ /** The addresses the crawl reached AND scanned. */
7
+ readonly visitedRoutes: readonly string[];
8
+ /** The addresses that answered, including those that forwarded off the public surface. */
9
+ readonly answeredRoutes: readonly string[];
10
+ readonly skippedPrefixes: readonly string[];
11
+ readonly specs: readonly SpecSource[];
12
+ readonly everyFile: readonly SpecSource[];
13
+ }
14
+ /** A declared page nothing looked at. */
15
+ export interface UnsweptRoute {
16
+ readonly file: string;
17
+ readonly route: string;
18
+ readonly rule: string;
19
+ readonly reason: string;
20
+ }
21
+ /**
22
+ * The declared pages the sweep did not cover and nothing else scans.
23
+ * @param sweep what the crawl reached, what the project declares, and where a scan would be written.
24
+ * @returns one violation per page nothing looked at, in the order the routes were declared.
25
+ */
26
+ export declare const findUnsweptRoutes: (sweep: RouteSweep) => readonly UnsweptRoute[];
27
+ //# sourceMappingURL=route-coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-coverage.d.ts","sourceRoot":"","sources":["../src/route-coverage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiD,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAClG,OAAO,EAAE,KAAK,aAAa,EAAgC,MAAM,gBAAgB,CAAA;AAqBjF,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,cAAc,EAAE,SAAS,aAAa,EAAE,CAAA;IACjD,mDAAmD;IACnD,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,0FAA0F;IAC1F,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1C,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3C,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAA;IACrC,QAAQ,CAAC,SAAS,EAAE,SAAS,UAAU,EAAE,CAAA;CAC1C;AAED,yCAAyC;AACzC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AA4DD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,UAAU,KAAG,SAAS,YAAY,EAMvE,CAAA"}
@@ -0,0 +1,57 @@
1
+ import { containsBuiltRoute, containsRoute, reachesAxe } from './axe-coverage.js';
2
+ import { matchesRoute, staticPrefixOf } from './route-map.js';
3
+ const REMEDIES = 'link it from a page the crawl reaches, scan it in a specification of your own with axe, or ' +
4
+ 'declare it in `ploaness.accessibilitySkipRoutes` with the reason it is out of scope';
5
+ // The same plain prefix test the crawl itself applies in `toRoute`, deliberately. A stricter one -
6
+ // requiring the prefix to end on a segment boundary - would report a route the crawl was told to skip
7
+ // and did skip, which is a finding about the harness disagreeing with itself.
8
+ const isSkipped = (route, prefixes) => prefixes.some((prefix) => route.startsWith(prefix));
9
+ // A page the crawl asked for, that answered, and that forwarded somewhere outside the public surface.
10
+ // There is nothing at that address for axe to judge - what renders is wherever it forwarded to, which
11
+ // has a declaration of its own - so a redirect is covered rather than unswept.
12
+ const isRedirect = (sweep, route) => sweep.answeredRoutes.some((answered) => matchesRoute(route.route, answered)) &&
13
+ !sweep.visitedRoutes.some((visited) => matchesRoute(route.route, visited));
14
+ const isScanned = (sweep, route) => sweep.visitedRoutes.some((visited) => matchesRoute(route.route, visited));
15
+ // What counts as a specification driving this route. A static address can be looked for as itself; a
16
+ // dynamic one cannot, because a spec never contains `/posts/[slug]` - it contains `/posts/` and then a
17
+ // value it computed. See `containsBuiltRoute` for why the prefix alone is not enough.
18
+ const isDrivenBy = (spec, route) => route.isDynamic
19
+ ? containsBuiltRoute(spec.source, staticPrefixOf(route.route))
20
+ : containsRoute(spec.source, route.route);
21
+ const unsweptRoute = (sweep, route) => {
22
+ const driving = sweep.specs.filter((spec) => isDrivenBy(spec, route));
23
+ if (driving.length === 0) {
24
+ return [
25
+ {
26
+ file: route.file,
27
+ route: route.route,
28
+ rule: 'route-unswept',
29
+ reason: `the accessibility sweep never reached "${route.route}", and no specification drives it ` +
30
+ `either, so this page has no accessibility coverage at all: ${REMEDIES}`,
31
+ },
32
+ ];
33
+ }
34
+ return driving.some((spec) => reachesAxe(spec, sweep.everyFile))
35
+ ? []
36
+ : [
37
+ {
38
+ file: route.file,
39
+ route: route.route,
40
+ rule: 'route-unscanned',
41
+ reason: `the accessibility sweep never reached "${route.route}". A specification drives it but ` +
42
+ `nothing scans it with axe, so this page has no accessibility coverage: ${REMEDIES}`,
43
+ },
44
+ ];
45
+ };
46
+ /**
47
+ * The declared pages the sweep did not cover and nothing else scans.
48
+ * @param sweep what the crawl reached, what the project declares, and where a scan would be written.
49
+ * @returns one violation per page nothing looked at, in the order the routes were declared.
50
+ */
51
+ export const findUnsweptRoutes = (sweep) => sweep.declaredRoutes.flatMap((route) => {
52
+ if (isSkipped(route.route, sweep.skippedPrefixes) || isScanned(sweep, route)) {
53
+ return [];
54
+ }
55
+ return isRedirect(sweep, route) ? [] : unsweptRoute(sweep, route);
56
+ });
57
+ //# sourceMappingURL=route-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-coverage.js","sourceRoot":"","sources":["../src/route-coverage.ts"],"names":[],"mappings":"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"}