@ploaness/governance 1.1.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.
- package/dist/a11y-hit-target.d.ts.map +1 -1
- package/dist/a11y-hit-target.js +1 -21
- package/dist/a11y-hit-target.js.map +1 -1
- package/dist/a11y-incomplete.d.ts.map +1 -1
- package/dist/a11y-incomplete.js +1 -18
- package/dist/a11y-incomplete.js.map +1 -1
- package/dist/access-boundary.d.ts +43 -1
- package/dist/access-boundary.d.ts.map +1 -1
- package/dist/access-boundary.js +146 -5
- package/dist/access-boundary.js.map +1 -1
- package/dist/admin-view-coverage.d.ts.map +1 -1
- package/dist/admin-view-coverage.js +1 -17
- package/dist/admin-view-coverage.js.map +1 -1
- package/dist/asset-policy.d.ts.map +1 -1
- package/dist/asset-policy.js +1 -23
- package/dist/asset-policy.js.map +1 -1
- package/dist/axe-coverage.d.ts +3 -4
- package/dist/axe-coverage.d.ts.map +1 -1
- package/dist/axe-coverage.js +18 -51
- package/dist/axe-coverage.js.map +1 -1
- package/dist/blocklist-entries.d.ts +36 -0
- package/dist/blocklist-entries.d.ts.map +1 -0
- package/dist/blocklist-entries.js +338 -0
- package/dist/blocklist-entries.js.map +1 -0
- package/dist/blocklist.d.ts +91 -0
- package/dist/blocklist.d.ts.map +1 -0
- package/dist/blocklist.js +187 -0
- package/dist/blocklist.js.map +1 -0
- package/dist/container-failure.d.ts.map +1 -1
- package/dist/container-failure.js +1 -18
- package/dist/container-failure.js.map +1 -1
- package/dist/container-files.d.ts +16 -0
- package/dist/container-files.d.ts.map +1 -1
- package/dist/container-files.js +23 -8
- package/dist/container-files.js.map +1 -1
- package/dist/container-images.d.ts +43 -0
- package/dist/container-images.d.ts.map +1 -0
- package/dist/container-images.js +157 -0
- package/dist/container-images.js.map +1 -0
- package/dist/dependency-freshness.d.ts +8 -0
- package/dist/dependency-freshness.d.ts.map +1 -1
- package/dist/dependency-freshness.js +2 -18
- package/dist/dependency-freshness.js.map +1 -1
- package/dist/doc-blocks.d.ts.map +1 -1
- package/dist/doc-blocks.js +1 -13
- package/dist/doc-blocks.js.map +1 -1
- package/dist/editorconfig-policy.d.ts +7 -0
- package/dist/editorconfig-policy.d.ts.map +1 -1
- package/dist/editorconfig-policy.js +19 -12
- package/dist/editorconfig-policy.js.map +1 -1
- package/dist/environment-coherence.d.ts.map +1 -1
- package/dist/environment-coherence.js +1 -23
- package/dist/environment-coherence.js.map +1 -1
- package/dist/environment-files.d.ts.map +1 -1
- package/dist/environment-files.js +1 -19
- package/dist/environment-files.js.map +1 -1
- package/dist/file-roles.d.ts +7 -2
- package/dist/file-roles.d.ts.map +1 -1
- package/dist/file-roles.js +10 -9
- package/dist/file-roles.js.map +1 -1
- package/dist/freshness-repair.d.ts +82 -0
- package/dist/freshness-repair.d.ts.map +1 -0
- package/dist/freshness-repair.js +91 -0
- package/dist/freshness-repair.js.map +1 -0
- package/dist/generated-denial.d.ts.map +1 -1
- package/dist/generated-denial.js +1 -9
- package/dist/generated-denial.js.map +1 -1
- package/dist/generated-drift.d.ts.map +1 -1
- package/dist/generated-drift.js +1 -12
- package/dist/generated-drift.js.map +1 -1
- package/dist/harness-package.d.ts +13 -0
- package/dist/harness-package.d.ts.map +1 -0
- package/dist/harness-package.js +20 -0
- package/dist/harness-package.js.map +1 -0
- package/dist/history-policy.d.ts.map +1 -1
- package/dist/history-policy.js +1 -7
- package/dist/history-policy.js.map +1 -1
- package/dist/image-assets.d.ts.map +1 -1
- package/dist/image-assets.js +1 -10
- package/dist/image-assets.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/install-policy.d.ts +21 -0
- package/dist/install-policy.d.ts.map +1 -1
- package/dist/install-policy.js +53 -1
- package/dist/install-policy.js.map +1 -1
- package/dist/jsx-accessibility-scope.d.ts +17 -0
- package/dist/jsx-accessibility-scope.d.ts.map +1 -0
- package/dist/jsx-accessibility-scope.js +36 -0
- package/dist/jsx-accessibility-scope.js.map +1 -0
- package/dist/jsx-accessibility.d.ts +13 -0
- package/dist/jsx-accessibility.d.ts.map +1 -0
- package/dist/jsx-accessibility.js +188 -0
- package/dist/jsx-accessibility.js.map +1 -0
- package/dist/network-policy.d.ts.map +1 -1
- package/dist/network-policy.js +1 -12
- package/dist/network-policy.js.map +1 -1
- package/dist/oxlint-policy.d.ts +16 -0
- package/dist/oxlint-policy.d.ts.map +1 -0
- package/dist/oxlint-policy.js +28 -0
- package/dist/oxlint-policy.js.map +1 -0
- package/dist/oxlint-report.d.ts +29 -0
- package/dist/oxlint-report.d.ts.map +1 -0
- package/dist/oxlint-report.js +84 -0
- package/dist/oxlint-report.js.map +1 -0
- package/dist/oxlint-scope.d.ts +13 -0
- package/dist/oxlint-scope.d.ts.map +1 -0
- package/dist/oxlint-scope.js +46 -0
- package/dist/oxlint-scope.js.map +1 -0
- package/dist/oxlint-suppressions.d.ts +17 -0
- package/dist/oxlint-suppressions.d.ts.map +1 -0
- package/dist/oxlint-suppressions.js +72 -0
- package/dist/oxlint-suppressions.js.map +1 -0
- package/dist/paired-assets.d.ts.map +1 -1
- package/dist/paired-assets.js.map +1 -1
- package/dist/payload-access.d.ts +6 -0
- package/dist/payload-access.d.ts.map +1 -1
- package/dist/payload-access.js +166 -9
- package/dist/payload-access.js.map +1 -1
- package/dist/payload-configs.d.ts +0 -1
- package/dist/payload-configs.d.ts.map +1 -1
- package/dist/payload-configs.js +36 -17
- package/dist/payload-configs.js.map +1 -1
- package/dist/payload-defaults.d.ts +128 -0
- package/dist/payload-defaults.d.ts.map +1 -0
- package/dist/payload-defaults.js +295 -0
- package/dist/payload-defaults.js.map +1 -0
- package/dist/payload-policy.d.ts +13 -0
- package/dist/payload-policy.d.ts.map +1 -1
- package/dist/payload-policy.js +81 -40
- package/dist/payload-policy.js.map +1 -1
- package/dist/relationship-cleanup.d.ts.map +1 -1
- package/dist/relationship-cleanup.js +16 -16
- package/dist/relationship-cleanup.js.map +1 -1
- package/dist/release-age.d.ts.map +1 -1
- package/dist/release-age.js +1 -16
- package/dist/release-age.js.map +1 -1
- package/dist/request-limits.d.ts.map +1 -1
- package/dist/request-limits.js +1 -11
- package/dist/request-limits.js.map +1 -1
- package/dist/route-map.d.ts.map +1 -1
- package/dist/route-map.js +1 -16
- package/dist/route-map.js.map +1 -1
- package/dist/run-plan.d.ts.map +1 -1
- package/dist/run-plan.js +1 -19
- package/dist/run-plan.js.map +1 -1
- package/dist/secret-policy.d.ts.map +1 -1
- package/dist/secret-policy.js +1 -11
- package/dist/secret-policy.js.map +1 -1
- package/dist/settings-layering.d.ts +8 -30
- package/dist/settings-layering.d.ts.map +1 -1
- package/dist/settings-layering.js +19 -48
- package/dist/settings-layering.js.map +1 -1
- package/dist/settings.d.ts +9 -2
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js.map +1 -1
- package/dist/source-text.d.ts +2 -0
- package/dist/source-text.d.ts.map +1 -1
- package/dist/source-text.js +7 -16
- package/dist/source-text.js.map +1 -1
- package/dist/toolchain-pins.d.ts.map +1 -1
- package/dist/toolchain-pins.js +2 -19
- package/dist/toolchain-pins.js.map +1 -1
- package/dist/version-policy.d.ts +7 -8
- package/dist/version-policy.d.ts.map +1 -1
- package/dist/version-policy.js +15 -26
- package/dist/version-policy.js.map +1 -1
- package/dist/vulnerability-policy.d.ts.map +1 -1
- package/dist/vulnerability-policy.js +1 -11
- package/dist/vulnerability-policy.js.map +1 -1
- package/dist/wiring-policy.d.ts.map +1 -1
- package/dist/wiring-policy.js +4 -1
- package/dist/wiring-policy.js.map +1 -1
- package/dist/wiring-violation.d.ts.map +1 -1
- package/dist/wiring-violation.js +1 -6
- package/dist/wiring-violation.js.map +1 -1
- package/dist/workspace-policy.d.ts.map +1 -1
- package/dist/workspace-policy.js +3 -9
- package/dist/workspace-policy.js.map +1 -1
- package/dist/yaml-blocks.d.ts +10 -0
- package/dist/yaml-blocks.d.ts.map +1 -1
- package/dist/yaml-blocks.js +20 -0
- package/dist/yaml-blocks.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
// Whether every collection and global in a BUILT Payload configuration decides its own access.
|
|
2
|
+
//
|
|
3
|
+
// The static rule in payload-access.ts reads the access block a project wrote, and it can only read what
|
|
4
|
+
// the project wrote. Payload builds collections the project never writes: the folder tree behind
|
|
5
|
+
// `folders`, the job queue behind `jobs`, the saved filters behind `queryPresets`, and whatever a plugin
|
|
6
|
+
// adds. Each arrives with the access Payload gives an undeclared operation, `defaultAccess`, which admits
|
|
7
|
+
// every signed-in user to every operation. The anonymous sweep cannot see that either, because a
|
|
8
|
+
// signed-in stakeholder is not anonymous. So a probe imports the built configuration and reports, per
|
|
9
|
+
// entity, which operations still carry that function; this module decides what the report means.
|
|
10
|
+
//
|
|
11
|
+
// Payload's own bookkeeping keeps the default on purpose, and those entities are exempt by name here,
|
|
12
|
+
// with the reason each. No project setting widens the list: a rule a project can edit is not a rule, and
|
|
13
|
+
// the repair is always available - write the access explicitly, through the override the framework
|
|
14
|
+
// offers for the entity it built.
|
|
15
|
+
import { asRecord, asText, isArray, isRecord, readKey } from './json-shapes.js';
|
|
16
|
+
/** The operations a collection must decide; the static rule holds a written config to the same list. */
|
|
17
|
+
export const COLLECTION_OPERATIONS = ['create', 'read', 'update', 'delete'];
|
|
18
|
+
/** The operations a global must decide. */
|
|
19
|
+
export const GLOBAL_OPERATIONS = ['read', 'update'];
|
|
20
|
+
/** The operation an auth collection decides on top of the four: who may clear a locked account. */
|
|
21
|
+
export const UNLOCK_OPERATION = 'unlock';
|
|
22
|
+
/** The operation a versioned collection or global decides: who may read a stored version. */
|
|
23
|
+
export const READ_VERSIONS_OPERATION = 'readVersions';
|
|
24
|
+
/**
|
|
25
|
+
* The access operations one entity owes a decision on. Two of them are conditional, and neither may be
|
|
26
|
+
* added to the lists above: Payload writes `unlock` onto EVERY collection during sanitisation, auth or
|
|
27
|
+
* not, so demanding it unconditionally would report the whole configuration, and `readVersions` means
|
|
28
|
+
* nothing on an entity that keeps no versions.
|
|
29
|
+
* @param traits what the entity is: its kind, and whether it authenticates or keeps versions.
|
|
30
|
+
* @returns the operations that entity must decide, in the order they are reported.
|
|
31
|
+
*/
|
|
32
|
+
export const accessOperationsFor = (traits) => [
|
|
33
|
+
...(traits.kind === 'collection' ? COLLECTION_OPERATIONS : GLOBAL_OPERATIONS),
|
|
34
|
+
...(traits.kind === 'collection' && traits.hasAuth ? [UNLOCK_OPERATION] : []),
|
|
35
|
+
...(traits.hasVersions ? [READ_VERSIONS_OPERATION] : []),
|
|
36
|
+
];
|
|
37
|
+
/**
|
|
38
|
+
* The prefix the probe prints its report behind. A plugin may log on import, and a line the parser can
|
|
39
|
+
* name is what separates the report from that chatter.
|
|
40
|
+
*/
|
|
41
|
+
export const INHERITED_ACCESS_REPORT_MARKER = 'ploaness-inherited-access:';
|
|
42
|
+
/** The entities exempt from the rule, each with the reason. Nothing a project declares extends this. */
|
|
43
|
+
export const EXEMPT_PAYLOAD_SUBJECTS = [
|
|
44
|
+
{
|
|
45
|
+
kind: 'collection',
|
|
46
|
+
slug: 'payload-locked-documents',
|
|
47
|
+
reason: 'the document-lock ledger, which declares the default deliberately so that any signed-in editor ' +
|
|
48
|
+
'can hold and release a lock',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
kind: 'collection',
|
|
52
|
+
slug: 'payload-preferences',
|
|
53
|
+
reason: 'per-user admin preferences; Payload scopes read and delete to the owner, and leaves create and ' +
|
|
54
|
+
'update to the default on purpose',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
kind: 'collection',
|
|
58
|
+
slug: 'payload-migrations',
|
|
59
|
+
reason: 'the migration ledger, with no endpoint and no GraphQL surface; only the framework writes it',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
kind: 'global',
|
|
63
|
+
slug: 'payload-jobs-stats',
|
|
64
|
+
reason: 'the scheduling counters Payload keeps beside the job queue; the framework offers no override ' +
|
|
65
|
+
'for it, so no explicit access can be written, and the queue it counts is judged',
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
/**
|
|
69
|
+
* The saved-filter collection Payload builds behind `enableQueryPresets`.
|
|
70
|
+
*
|
|
71
|
+
* It is named here because the probe has to read its access from somewhere other than the collection.
|
|
72
|
+
* `getAccess` wraps EVERY operation of this one collection in a closure that calls the project's rule
|
|
73
|
+
* where there is one and Payload's own default where there is not, so the built collection carries four
|
|
74
|
+
* functions that match neither the default by reference nor its source text. Read there, a project that
|
|
75
|
+
* decided nothing is indistinguishable from one that decided everything - and undecided means an
|
|
76
|
+
* unconstrained `create` for any signed-in user, and a preset shared as `everyone` readable by every
|
|
77
|
+
* role that can log in, the ones a project keeps outside its editorial staff included.
|
|
78
|
+
*/
|
|
79
|
+
export const QUERY_PRESETS_SLUG = 'payload-query-presets';
|
|
80
|
+
// Where the framework lets a project decide the access of an entity it built. Anything else is either
|
|
81
|
+
// the project's own collection, which has an access block, or a plugin's, which has an override option.
|
|
82
|
+
const FRAMEWORK_REPAIRS = {
|
|
83
|
+
'payload-folders': 'folders.collectionOverrides',
|
|
84
|
+
'payload-jobs': 'jobs.jobsCollectionOverrides',
|
|
85
|
+
[QUERY_PRESETS_SLUG]: 'queryPresets.access',
|
|
86
|
+
};
|
|
87
|
+
const isExempt = (kind, slug) => EXEMPT_PAYLOAD_SUBJECTS.some((subject) => subject.kind === kind && subject.slug === slug);
|
|
88
|
+
const repairFor = (slug, pronoun) => {
|
|
89
|
+
const framework = FRAMEWORK_REPAIRS[slug];
|
|
90
|
+
return framework === undefined
|
|
91
|
+
? `decide ${pronoun} in its access block, or in the override its plugin offers`
|
|
92
|
+
: `decide ${pronoun} in ${framework}`;
|
|
93
|
+
};
|
|
94
|
+
const describeInherited = (kind, entry) => {
|
|
95
|
+
const pronoun = entry.inherited.length === 1 ? 'it' : 'them';
|
|
96
|
+
return (`${kind} "${entry.slug}" leaves ${entry.inherited.join(', ')} to Payload's default access, ` +
|
|
97
|
+
`which admits every signed-in user; ${repairFor(entry.slug, pronoun)}`);
|
|
98
|
+
};
|
|
99
|
+
const judge = (kind, entries) => entries
|
|
100
|
+
.filter((entry) => entry.inherited.length > 0 && !isExempt(kind, entry.slug))
|
|
101
|
+
.map((entry) => describeInherited(kind, entry));
|
|
102
|
+
/**
|
|
103
|
+
* Every entity of the built configuration that still carries Payload's default access on a judged
|
|
104
|
+
* operation, in configuration order, each naming where the project decides it.
|
|
105
|
+
* @param report what the probe read from the built configuration.
|
|
106
|
+
* @returns one finding per entity, or nothing when every entity decides its access.
|
|
107
|
+
*/
|
|
108
|
+
export const findInheritedAccess = (report) => [
|
|
109
|
+
...judge('collection', report.collections),
|
|
110
|
+
...judge('global', report.globals),
|
|
111
|
+
];
|
|
112
|
+
// The read a drafts-enabled entity grants a stranger.
|
|
113
|
+
//
|
|
114
|
+
// A draft is a document nobody has approved. Payload writes an unpublished save to the versions table
|
|
115
|
+
// and leaves the main row alone, so a document that has never been published carries `_status: 'draft'`
|
|
116
|
+
// in the row a plain list reads - which means an unconstrained read serves unapproved content through
|
|
117
|
+
// the ordinary endpoint, not only through `?draft=true`.
|
|
118
|
+
//
|
|
119
|
+
// The static rule in payload-access.ts can only catch the inline spelling `read: (): boolean => true`,
|
|
120
|
+
// and the shipped ESLint config forbids exactly that spelling in a config file, so a conforming project
|
|
121
|
+
// writes `read: someHelper` and the static rule is blind to it. The anonymous sweep is blind too: it
|
|
122
|
+
// separates a grant that carries a query constraint from one that does not, and never opens the
|
|
123
|
+
// constraint to see what it constrains. Between the two, a read filtered by audience but not by status
|
|
124
|
+
// passed everything. This is the rule that reads the constraint.
|
|
125
|
+
/** The status Payload stamps on the version that is live. */
|
|
126
|
+
const PUBLISHED_STATUS = 'published';
|
|
127
|
+
/** The field Payload adds to an entity that keeps drafts. */
|
|
128
|
+
const STATUS_FIELD = '_status';
|
|
129
|
+
const declaresPublished = (where) => {
|
|
130
|
+
const status = where[STATUS_FIELD];
|
|
131
|
+
return isRecord(status) && status['equals'] === PUBLISHED_STATUS;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Whether a read filter admits published documents alone.
|
|
135
|
+
*
|
|
136
|
+
* A clause under `and` is enough on its own, because every conjunct holds. A clause under `or` counts
|
|
137
|
+
* only when EVERY branch carries one, because a single branch without it is a way in - which is the
|
|
138
|
+
* shape of the defect this rule exists for.
|
|
139
|
+
* @param where the query constraint the read rule returned.
|
|
140
|
+
* @returns whether every document the filter admits is a published one.
|
|
141
|
+
*/
|
|
142
|
+
export const requiresPublishedStatus = (where) => {
|
|
143
|
+
if (!isRecord(where)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
if (declaresPublished(where)) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
const conjuncts = where['and'];
|
|
150
|
+
if (isArray(conjuncts) &&
|
|
151
|
+
conjuncts.some((one) => requiresPublishedStatus(one))) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
const disjuncts = where['or'];
|
|
155
|
+
return (isArray(disjuncts) &&
|
|
156
|
+
disjuncts.length > 0 &&
|
|
157
|
+
disjuncts.every((one) => requiresPublishedStatus(one)));
|
|
158
|
+
};
|
|
159
|
+
const DRAFT_READ_REPAIR = 'a read that keeps unapproved work from a stranger either refuses them outright or filters on ' +
|
|
160
|
+
`${STATUS_FIELD} equals ${PUBLISHED_STATUS}`;
|
|
161
|
+
const describeDraftRead = (entry) => {
|
|
162
|
+
const subject = `${entry.kind} "${entry.slug}" keeps drafts and`;
|
|
163
|
+
const read = entry.anonymousRead;
|
|
164
|
+
if (read.kind === 'open') {
|
|
165
|
+
return (`${subject} grants an unconditional anonymous read, so every unapproved document in it is ` +
|
|
166
|
+
`served to anyone; ${DRAFT_READ_REPAIR}`);
|
|
167
|
+
}
|
|
168
|
+
if (read.kind === 'undecidable') {
|
|
169
|
+
return (`${subject} its read rule could not be decided from the request alone (${read.reason}); an ` +
|
|
170
|
+
'access rule is a pure decision over the caller it is handed, so the harness can read it');
|
|
171
|
+
}
|
|
172
|
+
if (read.kind === 'filtered' && !requiresPublishedStatus(read.where)) {
|
|
173
|
+
return (`${subject} filters its anonymous read without constraining ${STATUS_FIELD}, so an ` +
|
|
174
|
+
`unapproved document is served to anyone the filter admits; ${DRAFT_READ_REPAIR}`);
|
|
175
|
+
}
|
|
176
|
+
return undefined;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Every entity that keeps drafts and lets a stranger read something that was never approved, in
|
|
180
|
+
* configuration order.
|
|
181
|
+
* @param report what the probe read from the built configuration.
|
|
182
|
+
* @returns one finding per entity, or nothing when no drafts read admits an unapproved document.
|
|
183
|
+
*/
|
|
184
|
+
export const findUnconstrainedDraftReads = (report) => report.draftReads.flatMap((entry) => {
|
|
185
|
+
const finding = describeDraftRead(entry);
|
|
186
|
+
return finding === undefined ? [] : [finding];
|
|
187
|
+
});
|
|
188
|
+
const asEntry = (raw) => {
|
|
189
|
+
if (!isRecord(raw)) {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
const slug = raw['slug'];
|
|
193
|
+
const inherited = raw['inherited'];
|
|
194
|
+
if (typeof slug !== 'string' || !isArray(inherited)) {
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
return inherited.every((operation) => typeof operation === 'string')
|
|
198
|
+
? { slug, inherited: inherited.map((operation) => asText(operation)) }
|
|
199
|
+
: undefined;
|
|
200
|
+
};
|
|
201
|
+
const asEntries = (raw) => {
|
|
202
|
+
if (!isArray(raw)) {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
const entries = raw.map((entry) => asEntry(entry));
|
|
206
|
+
return entries.every((entry) => entry !== undefined)
|
|
207
|
+
? entries
|
|
208
|
+
: undefined;
|
|
209
|
+
};
|
|
210
|
+
const asAnonymousRead = (raw) => {
|
|
211
|
+
const kind = readKey(raw, 'kind');
|
|
212
|
+
if (kind === 'denied') {
|
|
213
|
+
return { kind };
|
|
214
|
+
}
|
|
215
|
+
if (kind === 'open') {
|
|
216
|
+
return { kind };
|
|
217
|
+
}
|
|
218
|
+
if (kind === 'filtered') {
|
|
219
|
+
return { kind, where: readKey(raw, 'where') };
|
|
220
|
+
}
|
|
221
|
+
return kind === 'undecidable' ? { kind, reason: asText(readKey(raw, 'reason')) } : undefined;
|
|
222
|
+
};
|
|
223
|
+
const asDraftRead = (raw) => {
|
|
224
|
+
const slug = readKey(raw, 'slug');
|
|
225
|
+
const kind = readKey(raw, 'kind');
|
|
226
|
+
const anonymousRead = asAnonymousRead(readKey(raw, 'anonymousRead'));
|
|
227
|
+
if (typeof slug !== 'string' || anonymousRead === undefined) {
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
return kind === 'collection' || kind === 'global' ? { kind, slug, anonymousRead } : undefined;
|
|
231
|
+
};
|
|
232
|
+
const asDraftReads = (raw) => {
|
|
233
|
+
if (!isArray(raw)) {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
const entries = raw.map((entry) => asDraftRead(entry));
|
|
237
|
+
return entries.every((entry) => entry !== undefined)
|
|
238
|
+
? entries
|
|
239
|
+
: undefined;
|
|
240
|
+
};
|
|
241
|
+
const parseJson = (text) => {
|
|
242
|
+
try {
|
|
243
|
+
return JSON.parse(text);
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* Read the probe's report out of everything the probe wrote to standard output.
|
|
251
|
+
*
|
|
252
|
+
* The LAST marker line is the report, so a plugin that logs during import cannot hide it, and a report
|
|
253
|
+
* that fails to parse or has the wrong shape is undefined rather than empty: a probe that printed a
|
|
254
|
+
* stack trace must fail the gate, never pass it as "no findings".
|
|
255
|
+
* @param text the probe's standard output.
|
|
256
|
+
* @returns the report, or undefined when no well-formed report was printed.
|
|
257
|
+
*/
|
|
258
|
+
export const parseInheritedAccessReport = (text) => {
|
|
259
|
+
const line = text
|
|
260
|
+
.split('\n')
|
|
261
|
+
.map((candidate) => candidate.trim())
|
|
262
|
+
.findLast((candidate) => candidate.startsWith(INHERITED_ACCESS_REPORT_MARKER));
|
|
263
|
+
if (line === undefined) {
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
const parsed = parseJson(line.slice(INHERITED_ACCESS_REPORT_MARKER.length));
|
|
267
|
+
const collections = asEntries(readKey(parsed, 'collections'));
|
|
268
|
+
const globals = asEntries(readKey(parsed, 'globals'));
|
|
269
|
+
const draftReads = asDraftReads(readKey(parsed, 'draftReads'));
|
|
270
|
+
if (collections === undefined || globals === undefined || draftReads === undefined) {
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
return { collections, draftReads, globals };
|
|
274
|
+
};
|
|
275
|
+
/** Where a Payload member keeps its configuration when its tsconfig says nothing else. */
|
|
276
|
+
export const DEFAULT_PAYLOAD_CONFIG_PATH = 'src/payload.config.ts';
|
|
277
|
+
const CURRENT_DIRECTORY_PREFIX = './';
|
|
278
|
+
/**
|
|
279
|
+
* The path of the Payload configuration a member's tsconfig aliases as `@payload-config`, which is the
|
|
280
|
+
* alias `ploaness init` writes and Payload's own CLI resolves through.
|
|
281
|
+
* @param tsconfig the parsed tsconfig, or anything when it could not be read.
|
|
282
|
+
* @returns the configured path relative to the member root, or the default path.
|
|
283
|
+
*/
|
|
284
|
+
export const payloadConfigPathOf = (tsconfig) => {
|
|
285
|
+
const paths = asRecord(readKey(readKey(tsconfig, 'compilerOptions'), 'paths'));
|
|
286
|
+
const alias = paths['@payload-config'];
|
|
287
|
+
const first = isArray(alias) ? alias[0] : undefined;
|
|
288
|
+
if (typeof first !== 'string' || first.length === 0) {
|
|
289
|
+
return DEFAULT_PAYLOAD_CONFIG_PATH;
|
|
290
|
+
}
|
|
291
|
+
return first.startsWith(CURRENT_DIRECTORY_PREFIX)
|
|
292
|
+
? first.slice(CURRENT_DIRECTORY_PREFIX.length)
|
|
293
|
+
: first;
|
|
294
|
+
};
|
|
295
|
+
//# sourceMappingURL=payload-defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-defaults.js","sourceRoot":"","sources":["../src/payload-defaults.ts"],"names":[],"mappings":"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"}
|
package/dist/payload-policy.d.ts
CHANGED
|
@@ -15,4 +15,17 @@ export declare const findSourceViolations: (source: string) => readonly PayloadV
|
|
|
15
15
|
* @returns one violation per offending line.
|
|
16
16
|
*/
|
|
17
17
|
export declare const findPayloadViolations: (source: string) => readonly PayloadViolation[];
|
|
18
|
+
/**
|
|
19
|
+
* The access decision a route handler has to state rather than inherit.
|
|
20
|
+
*
|
|
21
|
+
* The two rules that already read `overrideAccess` judge a value that is written down: one bans `true`,
|
|
22
|
+
* the other requires `false` beside a `user`. Neither can see an absent property, and absent is the
|
|
23
|
+
* dangerous spelling - Payload defaults `overrideAccess` to `true`, so the call that says nothing is
|
|
24
|
+
* the call that runs as an administrator. Under `src/endpoints` the omission is therefore the
|
|
25
|
+
* violation, which is why this rule reads the path and the others do not.
|
|
26
|
+
* @param filePath the repository-relative path of the file, which decides whether the rule applies.
|
|
27
|
+
* @param source the file's text.
|
|
28
|
+
* @returns one violation per Local API call that leaves the decision to the default.
|
|
29
|
+
*/
|
|
30
|
+
export declare const findEndpointViolations: (filePath: string, source: string) => readonly PayloadViolation[];
|
|
18
31
|
//# sourceMappingURL=payload-policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payload-policy.d.ts","sourceRoot":"","sources":["../src/payload-policy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"payload-policy.d.ts","sourceRoot":"","sources":["../src/payload-policy.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAqT3D;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,KAAG,SAAS,gBAAgB,EAG9E,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,KAAG,SAAS,gBAAgB,EAmB/E,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GACjC,UAAU,MAAM,EAChB,QAAQ,MAAM,KACb,SAAS,gBAAgB,EAiB3B,CAAA"}
|
package/dist/payload-policy.js
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
// Payload
|
|
2
|
-
|
|
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';
|
|
14
3
|
import { findUnprotectedPrivilegedFields } from './payload-field-access.js';
|
|
15
4
|
import { findFailOpenSecretGuards } from './source-security.js';
|
|
16
5
|
import { balancedArguments, lineOf, occurrences, stripComments, topLevelSlice, } from './source-text.js';
|
|
@@ -36,16 +25,9 @@ const BOUNDED_CALLS = [
|
|
|
36
25
|
reason: 'bound payload.findGlobal() with an explicit depth to cap relationship population',
|
|
37
26
|
},
|
|
38
27
|
];
|
|
39
|
-
//
|
|
40
|
-
// `array.find(...)` is never touched. `req` may itself be reached through a chain - `ctx.req.payload`
|
|
41
|
-
// inside a hook is as much the Payload instance as `req.payload` - and excluding a preceding dot meant
|
|
42
|
-
// every such call went unjudged. The chain is admitted only in front of `req`, so a property named
|
|
43
|
-
// `payload` on some unrelated object is still left alone.
|
|
28
|
+
// Recognized Payload receivers exclude unrelated collection methods such as array.find().
|
|
44
29
|
const PAYLOAD_RECEIVER = /(?:^|[^\w$.])(?:payload|(?:[\w$]+\.)*req\.payload|this\.payload)$/;
|
|
45
|
-
|
|
46
|
-
// A shorthand property has no colon, so `declaresKey` cannot see it. The property boundary must be the
|
|
47
|
-
// start of the literal or a comma: admitting arbitrary whitespace made the `req` VALUE in
|
|
48
|
-
// `request: req` look like a `req` PROPERTY and vouched for a transaction the call never received.
|
|
30
|
+
// A property boundary keeps a value such as request: req from being read as a req property.
|
|
49
31
|
const declaresProperty = (topLevel, key) => new RegExp(String.raw `(?:^|,)\s*${key}\s*(?::|,|$)`).test(topLevel);
|
|
50
32
|
// The depth-one text of a call's options literal, or undefined when the call is not one to judge.
|
|
51
33
|
const topLevelOptionsAt = (source, call, found, receiver) => {
|
|
@@ -53,23 +35,20 @@ const topLevelOptionsAt = (source, call, found, receiver) => {
|
|
|
53
35
|
return undefined;
|
|
54
36
|
}
|
|
55
37
|
const argumentText = balancedArguments(source, found + call.length - 1);
|
|
56
|
-
return argumentText?.
|
|
38
|
+
return argumentText?.trim().startsWith('{') === true ? topLevelSlice(argumentText) : undefined;
|
|
57
39
|
};
|
|
58
|
-
// A
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const optionKeysAt = (source, call, found, receiver) => {
|
|
66
|
-
const topLevel = topLevelOptionsAt(source, call, found, receiver);
|
|
67
|
-
return topLevel === undefined || topLevel.includes('...') ? undefined : topLevel;
|
|
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('...');
|
|
68
47
|
};
|
|
69
48
|
const unboundedCallAt = (source, rule, found) => {
|
|
70
|
-
const topLevel =
|
|
49
|
+
const topLevel = topLevelOptionsAt(source, rule.call, found, PAYLOAD_RECEIVER);
|
|
71
50
|
if (topLevel === undefined ||
|
|
72
|
-
rule.required.some((key) =>
|
|
51
|
+
rule.required.some((key) => hasEffectiveProperty(topLevel, key))) {
|
|
73
52
|
return undefined;
|
|
74
53
|
}
|
|
75
54
|
return { line: lineOf(source, found), rule: rule.rule, reason: rule.reason };
|
|
@@ -103,9 +82,20 @@ const LOCAL_API_CALLS = [
|
|
|
103
82
|
// has no request to thread, and demanding one would be asking for a value that does not exist. Reaching
|
|
104
83
|
// the instance THROUGH `req` is the proof that one does, so the omission is never a decision.
|
|
105
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
|
+
: []));
|
|
106
96
|
const unthreadedCallAt = (source, call, found) => {
|
|
107
|
-
const topLevel =
|
|
108
|
-
if (topLevel === undefined ||
|
|
97
|
+
const topLevel = topLevelOptionsAt(source, call, found, REQUEST_RECEIVER);
|
|
98
|
+
if (topLevel === undefined || hasEffectiveProperty(topLevel, 'req')) {
|
|
109
99
|
return undefined;
|
|
110
100
|
}
|
|
111
101
|
return {
|
|
@@ -138,7 +128,7 @@ const hasEffectiveAccessControl = (topLevel) => {
|
|
|
138
128
|
const userAccessViolationAt = (source, call, found) => {
|
|
139
129
|
const topLevel = topLevelOptionsAt(source, call, found, PAYLOAD_RECEIVER);
|
|
140
130
|
if (topLevel === undefined ||
|
|
141
|
-
!declaresProperty(topLevel, 'user') ||
|
|
131
|
+
!(declaresProperty(topLevel, 'user') || topLevel.includes('...')) ||
|
|
142
132
|
hasEffectiveAccessControl(topLevel) ||
|
|
143
133
|
overrideProperties(topLevel).some((property) => property.value === 'true')) {
|
|
144
134
|
return undefined;
|
|
@@ -146,7 +136,8 @@ const userAccessViolationAt = (source, call, found) => {
|
|
|
146
136
|
return {
|
|
147
137
|
line: lineOf(source, found),
|
|
148
138
|
rule: 'require-user-access-control',
|
|
149
|
-
reason: `set overrideAccess: false on ${call.slice(1, -1)}()
|
|
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 ' +
|
|
150
141
|
'runs the operation as an administrator and ignores that user for access control',
|
|
151
142
|
};
|
|
152
143
|
};
|
|
@@ -157,8 +148,31 @@ const OVERRIDE_ACCESS = /overrideAccess\s*:\s*true/g;
|
|
|
157
148
|
const findOverrideAccess = (source) => [...source.matchAll(OVERRIDE_ACCESS)].map((match) => ({
|
|
158
149
|
line: lineOf(source, match.index),
|
|
159
150
|
rule: 'no-override-access',
|
|
160
|
-
reason: 'overrideAccess: true bypasses Payload access control;
|
|
151
|
+
reason: 'overrideAccess: true bypasses Payload access control; set overrideAccess: false so the ' +
|
|
152
|
+
'access rules run against the calling user',
|
|
161
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
|
+
};
|
|
162
176
|
// Anchored on the keyword that immediately precedes the specifier rather than on the statement that
|
|
163
177
|
// opens it. Requiring the whole statement to fit on one line missed a multi-line brace list, which is
|
|
164
178
|
// how a long import is normally written, and requiring `from` missed both a side-effect import and the
|
|
@@ -208,15 +222,42 @@ export const findPayloadViolations = (source) => {
|
|
|
208
222
|
// order to explain why the code avoids it must not be reported as that construct.
|
|
209
223
|
const code = stripComments(source);
|
|
210
224
|
return [
|
|
225
|
+
...findOpaqueOptions(code),
|
|
211
226
|
...findUnboundedCalls(code),
|
|
212
227
|
...findUnthreadedRequests(code),
|
|
213
228
|
...findIgnoredUsers(code),
|
|
214
229
|
...findOverrideAccess(code),
|
|
215
230
|
...findUndeclaredAccess(code),
|
|
216
231
|
...findUnhardenedAuth(code),
|
|
232
|
+
...findUnlockableAuth(code),
|
|
217
233
|
...findAnonymousDraftReads(code),
|
|
234
|
+
...findUndeclaredVersionReads(code),
|
|
218
235
|
...findUnrestrictedUploads(code),
|
|
236
|
+
...findUndecidedSvgHeaders(code),
|
|
219
237
|
...findUnprotectedPrivilegedFields(code),
|
|
220
238
|
];
|
|
221
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
|
+
};
|
|
222
263
|
//# sourceMappingURL=payload-policy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payload-policy.js","sourceRoot":"","sources":["../src/payload-policy.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"payload-policy.js","sourceRoot":"","sources":["../src/payload-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-cleanup.d.ts","sourceRoot":"","sources":["../src/relationship-cleanup.ts"],"names":[],"mappings":"
|
|
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"}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
// `payload-access.ts`. A global has no relationship column of its own to constrain.
|
|
5
|
-
const COLLECTION_DECLARATION = /(:|satisfies)\s*CollectionConfig(?=[<=,)\s]|$)/g;
|
|
6
|
-
const SATISFIES = 'satisfies';
|
|
1
|
+
import { payloadConfigsIn } from './payload-configs.js';
|
|
2
|
+
import { depthOneBlockKeys, depthOneValue } from './payload-source.js';
|
|
3
|
+
import { enclosingLiteral, occurrences, stripComments } from './source-text.js';
|
|
7
4
|
// The two field types that put a foreign key on the table rather than in the join table. `upload` is
|
|
8
5
|
// one of them: it is a relationship to the media collection wearing a different name.
|
|
9
6
|
const KEYED_FIELD_TYPES = ['relationship', 'upload'];
|
|
@@ -21,21 +18,24 @@ const slugOf = (body) => QUOTED_VALUE.exec(depthOneValue(body, 'slug') ?? '')?.[
|
|
|
21
18
|
// A collection with drafts enabled gets no null constraint anywhere, so none of its required fields can
|
|
22
19
|
// reach the contradiction.
|
|
23
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
|
+
//
|
|
24
26
|
// Comments are blanked before anything is read, the way every other Payload rule reads a file. The keys
|
|
25
27
|
// this rule looks for sit at depth one, and a depth-one key is recognised by the delimiter in front of
|
|
26
28
|
// it - so a comment between the previous entry and `hooks:` hid the hook, and the rule reported a
|
|
27
29
|
// collection that was guarded. `stripComments` preserves offsets and newlines, so the reported line is
|
|
28
30
|
// still the line of the source.
|
|
29
|
-
const collectionsIn = (file) =>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
38
|
-
};
|
|
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
39
|
// Every field of one collection that puts a NOT NULL column against another collection's primary key.
|
|
40
40
|
// The field literal is recovered from the offset of its own `type` key, so a relationship nested inside
|
|
41
41
|
// an array or a group is read exactly like one declared at the top: both get a column, on their own
|