@intentius/chant-lexicon-cedar 0.44.8 → 0.44.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/dist/codegen/package.d.ts.map +1 -1
- package/dist/config.d.ts +25 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/dogwood/cli.d.ts +206 -0
- package/dist/dogwood/cli.d.ts.map +1 -0
- package/dist/dogwood/event-schema.d.ts +161 -0
- package/dist/dogwood/event-schema.d.ts.map +1 -0
- package/dist/dogwood/index.d.ts +33 -0
- package/dist/dogwood/index.d.ts.map +1 -0
- package/dist/dogwood/macros.d.ts +96 -0
- package/dist/dogwood/macros.d.ts.map +1 -0
- package/dist/dogwood/policy.d.ts +120 -0
- package/dist/dogwood/policy.d.ts.map +1 -0
- package/dist/dogwood/scan.d.ts +109 -0
- package/dist/dogwood/scan.d.ts.map +1 -0
- package/dist/dogwood/serialize.d.ts +46 -0
- package/dist/dogwood/serialize.d.ts.map +1 -0
- package/dist/dogwood/temporal.d.ts +259 -0
- package/dist/dogwood/temporal.d.ts.map +1 -0
- package/dist/dogwood/upstream.d.ts +41 -0
- package/dist/dogwood/upstream.d.ts.map +1 -0
- package/dist/dogwood/window.d.ts +73 -0
- package/dist/dogwood/window.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +13 -3
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/dogwood-helpers.d.ts +63 -0
- package/dist/lint/post-synth/dogwood-helpers.d.ts.map +1 -0
- package/dist/lint/post-synth/dwdc010.d.ts +25 -0
- package/dist/lint/post-synth/dwdc010.d.ts.map +1 -0
- package/dist/lint/post-synth/dwdc011.d.ts +19 -0
- package/dist/lint/post-synth/dwdc011.d.ts.map +1 -0
- package/dist/lint/post-synth/dwdc012.d.ts +21 -0
- package/dist/lint/post-synth/dwdc012.d.ts.map +1 -0
- package/dist/lint/post-synth/dwde010.d.ts +32 -0
- package/dist/lint/post-synth/dwde010.d.ts.map +1 -0
- package/dist/lint/post-synth/dwde011.d.ts +33 -0
- package/dist/lint/post-synth/dwde011.d.ts.map +1 -0
- package/dist/lint/post-synth/dwds010.d.ts +24 -0
- package/dist/lint/post-synth/dwds010.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/okf/index.md +6 -0
- package/dist/okf/rules/DWDC010.md +11 -0
- package/dist/okf/rules/DWDC011.md +11 -0
- package/dist/okf/rules/DWDC012.md +11 -0
- package/dist/okf/rules/DWDE010.md +11 -0
- package/dist/okf/rules/DWDE011.md +11 -0
- package/dist/okf/rules/DWDS010.md +11 -0
- package/dist/policy-text.d.ts +53 -0
- package/dist/policy-text.d.ts.map +1 -0
- package/dist/rules/dogwood-helpers.ts +139 -0
- package/dist/rules/dwdc010.ts +62 -0
- package/dist/rules/dwdc011.ts +61 -0
- package/dist/rules/dwdc012.ts +46 -0
- package/dist/rules/dwde010.ts +130 -0
- package/dist/rules/dwde011.ts +108 -0
- package/dist/rules/dwds010.ts +46 -0
- package/dist/serializer.d.ts +10 -18
- package/dist/serializer.d.ts.map +1 -1
- package/dist/skills/chant-cedar-authoring.md +180 -0
- package/dist/skills/chant-cedar-avp-embedding.md +125 -0
- package/dist/skills/chant-cedar-meta-policy.md +119 -0
- package/package.json +2 -2
- package/src/codegen/package.ts +3 -2
- package/src/config.test.ts +12 -0
- package/src/config.ts +28 -0
- package/src/dogwood/cli.test.ts +392 -0
- package/src/dogwood/cli.ts +545 -0
- package/src/dogwood/event-schema.test.ts +218 -0
- package/src/dogwood/event-schema.ts +318 -0
- package/src/dogwood/index.ts +198 -0
- package/src/dogwood/macros.test.ts +104 -0
- package/src/dogwood/macros.ts +229 -0
- package/src/dogwood/policy.test.ts +94 -0
- package/src/dogwood/policy.ts +141 -0
- package/src/dogwood/scan.ts +287 -0
- package/src/dogwood/serialize.test.ts +331 -0
- package/src/dogwood/serialize.ts +209 -0
- package/src/dogwood/temporal.test.ts +272 -0
- package/src/dogwood/temporal.ts +592 -0
- package/src/dogwood/testdata/custom-kinds.dwschema +17 -0
- package/src/dogwood/testdata/default-macros.dw +23 -0
- package/src/dogwood/testdata/lowered-read-after-login.json +13 -0
- package/src/dogwood/testdata/max-window-raised.dwschema +25 -0
- package/src/dogwood/testdata/pinned.dwschema +31 -0
- package/src/dogwood/testdata/read-after-login.cedarschema +20 -0
- package/src/dogwood/testdata/read-after-login.dw +17 -0
- package/src/dogwood/testdata/temporal-policies.dw +53 -0
- package/src/dogwood/upstream.ts +41 -0
- package/src/dogwood/window.ts +124 -0
- package/src/index.ts +24 -0
- package/src/lint/audit-catalog.ts +56 -0
- package/src/lint/post-synth/dogwood-helpers.ts +139 -0
- package/src/lint/post-synth/dwd-post-synth.test.ts +256 -0
- package/src/lint/post-synth/dwdc010.ts +62 -0
- package/src/lint/post-synth/dwdc011.ts +61 -0
- package/src/lint/post-synth/dwdc012.ts +46 -0
- package/src/lint/post-synth/dwde-post-synth.test.ts +368 -0
- package/src/lint/post-synth/dwde010.ts +130 -0
- package/src/lint/post-synth/dwde011.ts +108 -0
- package/src/lint/post-synth/dwds010.ts +46 -0
- package/src/lint/post-synth/index.ts +12 -0
- package/src/lint/post-synth/post-synth.test.ts +7 -3
- package/src/policy-text.ts +128 -0
- package/src/serializer.ts +71 -109
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning a build's emitted files into `dogwood` CLI invocations (#1659).
|
|
3
|
+
*
|
|
4
|
+
* The DWDE checks both need the same thing: which of the emitted `.dw` files
|
|
5
|
+
* is a policy set, which is a macro library, which `.cedarschema` backs
|
|
6
|
+
* `--policy-schema`, and which `.dwschema` backs `--event-schema`. Planning
|
|
7
|
+
* that once here keeps the two checks from disagreeing about what they ran —
|
|
8
|
+
* DWDE010 validates a bundle and DWDE011 lowers the *same* bundle, and a
|
|
9
|
+
* finding from one that names different inputs than the other would be a
|
|
10
|
+
* puzzle rather than a report.
|
|
11
|
+
*
|
|
12
|
+
* Excluded from check auto-discovery by the "helper" filename filter.
|
|
13
|
+
*/
|
|
14
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
15
|
+
import { blankComments, dogwoodPolicyFiles, dogwoodSchemaFiles, type DogwoodArtifact } from "../../dogwood/scan";
|
|
16
|
+
import type { DogwoodBundle } from "../../dogwood/cli";
|
|
17
|
+
|
|
18
|
+
/** One planned invocation: the files, and the names to blame in a finding. */
|
|
19
|
+
export interface PreparedBundle {
|
|
20
|
+
lexicon: string;
|
|
21
|
+
/** The `.dw` policy set filename. */
|
|
22
|
+
source: string;
|
|
23
|
+
/** The `.cedarschema` filename passed as `--policy-schema`. */
|
|
24
|
+
policySchemaSource: string;
|
|
25
|
+
/** The `.dwschema` filename passed as `--event-schema`, when one was emitted. */
|
|
26
|
+
eventSchemaSource?: string;
|
|
27
|
+
bundle: DogwoodBundle;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** What the CLI-gated checks should do with this build. */
|
|
31
|
+
export interface DogwoodRunPlan {
|
|
32
|
+
/** True when the build emitted a `.dw` policy set at all. */
|
|
33
|
+
hasPolicies: boolean;
|
|
34
|
+
/** Every invocation to make. Empty when {@link blocked} is set. */
|
|
35
|
+
bundles: PreparedBundle[];
|
|
36
|
+
/** Why nothing can be run, when policies exist but a required input does not. */
|
|
37
|
+
blocked?: string;
|
|
38
|
+
/** The lexicon the `.dw` output came from, for a finding that names no file. */
|
|
39
|
+
lexicon?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A `.dw` file holding only `def` declarations is a macro library, not a
|
|
44
|
+
* policy set.
|
|
45
|
+
*
|
|
46
|
+
* By structure rather than by filename: the serializer's default is
|
|
47
|
+
* `macros.dw`, but `MacroLibraryProps.filename` overrides it, and `chant
|
|
48
|
+
* audit` runs over trees chant never wrote. A policy set is a file with a
|
|
49
|
+
* `permit` or `forbid` head in it — upstream's policy grammar has no third
|
|
50
|
+
* effect — and a set with inlined macros still has one.
|
|
51
|
+
*/
|
|
52
|
+
export function looksLikeMacroLibrary(text: string): boolean {
|
|
53
|
+
return !/\b(permit|forbid)\s*\(/.test(blankComments(text));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function byName(a: DogwoodArtifact, b: DogwoodArtifact): number {
|
|
57
|
+
return a.source.localeCompare(b.source);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The Cedar action schema the CLI needs, as text.
|
|
62
|
+
*
|
|
63
|
+
* `--policy-schema` reads a human-readable `.cedarschema`; a `.cedarschema.json`
|
|
64
|
+
* is not a substitute, so it is not offered as one. Sorted by filename so a
|
|
65
|
+
* build emitting several picks the same one on every run.
|
|
66
|
+
*/
|
|
67
|
+
function findPolicySchema(ctx: PostSynthContext): DogwoodArtifact | undefined {
|
|
68
|
+
const found: DogwoodArtifact[] = [];
|
|
69
|
+
for (const [lexicon, output] of ctx.outputs) {
|
|
70
|
+
if (typeof output === "string") continue;
|
|
71
|
+
for (const [source, text] of Object.entries(output.files ?? {})) {
|
|
72
|
+
if (typeof text !== "string") continue;
|
|
73
|
+
if (!source.endsWith(".cedarschema")) continue;
|
|
74
|
+
found.push({ lexicon, source, text });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return found.sort(byName)[0];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Plan every `dogwood` invocation this build calls for.
|
|
82
|
+
*
|
|
83
|
+
* One bundle per policy set per emitted event schema. A build with several
|
|
84
|
+
* `.dwschema` files means several services, and `--event-schema` takes exactly
|
|
85
|
+
* one file, so the set is run against each and every finding names the schema
|
|
86
|
+
* it was found under — the alternative is picking one silently and reporting
|
|
87
|
+
* against a schema the reader did not choose. Macro libraries do combine: a
|
|
88
|
+
* library is a sequence of `def`s, so several concatenate into the one file
|
|
89
|
+
* `--macros` accepts.
|
|
90
|
+
*/
|
|
91
|
+
export function planDogwoodRuns(ctx: PostSynthContext): DogwoodRunPlan {
|
|
92
|
+
const files = dogwoodPolicyFiles(ctx).sort(byName);
|
|
93
|
+
const policySets = files.filter((f) => !looksLikeMacroLibrary(f.text));
|
|
94
|
+
if (policySets.length === 0) return { hasPolicies: false, bundles: [] };
|
|
95
|
+
|
|
96
|
+
const lexicon = policySets[0].lexicon;
|
|
97
|
+
const libraries = files.filter((f) => looksLikeMacroLibrary(f.text));
|
|
98
|
+
const macros = libraries.length > 0 ? libraries.map((f) => f.text).join("\n") : undefined;
|
|
99
|
+
|
|
100
|
+
const policySchema = findPolicySchema(ctx);
|
|
101
|
+
if (!policySchema) {
|
|
102
|
+
return {
|
|
103
|
+
hasPolicies: true,
|
|
104
|
+
bundles: [],
|
|
105
|
+
lexicon,
|
|
106
|
+
blocked:
|
|
107
|
+
"no Cedar action schema (.cedarschema) was emitted beside them, and `dogwood validate` requires one via --policy-schema",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const eventSchemas = dogwoodSchemaFiles(ctx).sort(byName);
|
|
112
|
+
const bundles: PreparedBundle[] = [];
|
|
113
|
+
|
|
114
|
+
for (const set of policySets) {
|
|
115
|
+
for (const events of eventSchemas.length > 0 ? eventSchemas : [undefined]) {
|
|
116
|
+
bundles.push({
|
|
117
|
+
lexicon: set.lexicon,
|
|
118
|
+
source: set.source,
|
|
119
|
+
policySchemaSource: policySchema.source,
|
|
120
|
+
...(events ? { eventSchemaSource: events.source } : {}),
|
|
121
|
+
bundle: {
|
|
122
|
+
policies: set.text,
|
|
123
|
+
policySchema: policySchema.text,
|
|
124
|
+
...(events ? { eventSchema: events.text } : {}),
|
|
125
|
+
...(macros !== undefined ? { macros } : {}),
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return { hasPolicies: true, bundles, lexicon };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** How a finding names the inputs a run was made with. */
|
|
135
|
+
export function describeBundle(prepared: PreparedBundle): string {
|
|
136
|
+
const parts = [`"${prepared.source}"`, `against "${prepared.policySchemaSource}"`];
|
|
137
|
+
if (prepared.eventSchemaSource) parts.push(`with "${prepared.eventSchemaSource}"`);
|
|
138
|
+
return parts.join(" ");
|
|
139
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DWDC010: a temporal predicate must name an event kind the schema declares
|
|
3
|
+
*
|
|
4
|
+
* Upstream rejects this outright — a predicate naming an undeclared kind fails
|
|
5
|
+
* `dogwood validate` with code `extension`: "predicate
|
|
6
|
+
* `Drupe::Action::\"Read\"::nosuchkind` does not name a declared event (no
|
|
7
|
+
* event kind `nosuchkind` derived for action `Read`)". Catching it in
|
|
8
|
+
* TypeScript is the point of the split epic #1646 drew: everything answerable
|
|
9
|
+
* without the binary runs always and gates, and this one is answerable.
|
|
10
|
+
*
|
|
11
|
+
* The check is on the event *kind*, not the action, and that is not a
|
|
12
|
+
* shortcut. Upstream's `.dwschema` grammar declares events against a symbolic
|
|
13
|
+
* action binder — `decision event <A>::request { … }` — so the file names no
|
|
14
|
+
* actions at all and cannot be asked whether one exists. The action half of
|
|
15
|
+
* that upstream error comes from the Cedar action schema
|
|
16
|
+
* (`--policy-schema`), which is #1659's CLI-gated leg.
|
|
17
|
+
*
|
|
18
|
+
* Silent when no `.dwschema` was emitted: with none supplied,
|
|
19
|
+
* `ServiceSchema::defaults()` decides the kinds at the far end, and guessing
|
|
20
|
+
* that a project's out-of-band schema matches upstream's default would fail
|
|
21
|
+
* builds for a policy set that is fine.
|
|
22
|
+
*/
|
|
23
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
24
|
+
import { dogwoodPolicyFiles, dogwoodSchemaFiles, readEventSchema, scanPredicates } from "../../dogwood/scan";
|
|
25
|
+
|
|
26
|
+
export const dwdc010: PostSynthCheck = {
|
|
27
|
+
id: "DWDC010",
|
|
28
|
+
description: "A dogwood temporal predicate names an event kind the emitted .dwschema declares",
|
|
29
|
+
|
|
30
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
31
|
+
const schemas = dogwoodSchemaFiles(ctx);
|
|
32
|
+
if (schemas.length === 0) return [];
|
|
33
|
+
|
|
34
|
+
const declared = new Set<string>();
|
|
35
|
+
for (const schema of schemas) {
|
|
36
|
+
for (const kind of readEventSchema(schema.text).kinds) declared.add(kind);
|
|
37
|
+
}
|
|
38
|
+
if (declared.size === 0) return [];
|
|
39
|
+
|
|
40
|
+
const known = [...declared].sort().join(", ");
|
|
41
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
42
|
+
|
|
43
|
+
for (const policies of dogwoodPolicyFiles(ctx)) {
|
|
44
|
+
const seen = new Set<string>();
|
|
45
|
+
for (const ref of scanPredicates(policies.text)) {
|
|
46
|
+
if (declared.has(ref.kind)) continue;
|
|
47
|
+
const key = `${ref.action}::${ref.kind}`;
|
|
48
|
+
if (seen.has(key)) continue;
|
|
49
|
+
seen.add(key);
|
|
50
|
+
diagnostics.push({
|
|
51
|
+
checkId: "DWDC010",
|
|
52
|
+
severity: "error",
|
|
53
|
+
message: `Dogwood policy set "${policies.source}" has a temporal predicate on ${key}, but the emitted event schema declares no event kind "${ref.kind}" (declared: ${known}).`,
|
|
54
|
+
entity: policies.source,
|
|
55
|
+
lexicon: policies.lexicon,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return diagnostics;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DWDC011: a temporal window must fit inside the schema's `max_window`
|
|
3
|
+
*
|
|
4
|
+
* `formerly within 48h` under a 24h cap is an upstream validation error —
|
|
5
|
+
* "temporal window `48h` exceeds the maximum allowed window `24h` set by the
|
|
6
|
+
* event schema's `max_window`" — and it is arithmetic, so it needs no binary.
|
|
7
|
+
*
|
|
8
|
+
* Unlike DWDC010 this fires with no `.dwschema` emitted, because the cap
|
|
9
|
+
* applies either way: `ServiceSchema::defaults()` caps look-back at 24h, and
|
|
10
|
+
* so does an emitted schema that omits the directive. Where several schemas
|
|
11
|
+
* are emitted the tightest cap wins — a window one consumer accepts and
|
|
12
|
+
* another rejects is still a window that will be rejected.
|
|
13
|
+
*
|
|
14
|
+
* Macro-call intervals count. `once(48h, …)` expands to `formerly within 48h`
|
|
15
|
+
* through the default library's `within ?w`, so it looks back exactly as far.
|
|
16
|
+
*/
|
|
17
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
18
|
+
import {
|
|
19
|
+
dogwoodPolicyFiles,
|
|
20
|
+
dogwoodSchemaFiles,
|
|
21
|
+
effectiveMaxWindowSeconds,
|
|
22
|
+
readEventSchema,
|
|
23
|
+
scanWindows,
|
|
24
|
+
} from "../../dogwood/scan";
|
|
25
|
+
|
|
26
|
+
export const dwdc011: PostSynthCheck = {
|
|
27
|
+
id: "DWDC011",
|
|
28
|
+
description: "A dogwood temporal window stays inside the event schema's max_window cap",
|
|
29
|
+
|
|
30
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
31
|
+
const policyFiles = dogwoodPolicyFiles(ctx);
|
|
32
|
+
if (policyFiles.length === 0) return [];
|
|
33
|
+
|
|
34
|
+
const schemas = dogwoodSchemaFiles(ctx).map((s) => readEventSchema(s.text));
|
|
35
|
+
const cap = effectiveMaxWindowSeconds(schemas);
|
|
36
|
+
const source =
|
|
37
|
+
schemas.length === 0
|
|
38
|
+
? "upstream's default cap (no event schema was emitted)"
|
|
39
|
+
: `the emitted event schema's max_window`;
|
|
40
|
+
|
|
41
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
42
|
+
|
|
43
|
+
for (const policies of policyFiles) {
|
|
44
|
+
const reported = new Set<string>();
|
|
45
|
+
for (const window of scanWindows(policies.text)) {
|
|
46
|
+
if (window.seconds <= cap.seconds) continue;
|
|
47
|
+
if (reported.has(window.text)) continue;
|
|
48
|
+
reported.add(window.text);
|
|
49
|
+
diagnostics.push({
|
|
50
|
+
checkId: "DWDC011",
|
|
51
|
+
severity: "error",
|
|
52
|
+
message: `Dogwood policy set "${policies.source}" looks back ${window.text}, past the ${cap.text} allowed by ${source}. Raise max_window in the event schema, or shorten the window.`,
|
|
53
|
+
entity: policies.source,
|
|
54
|
+
lexicon: policies.lexicon,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return diagnostics;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DWDC012: `formerly`, `previous` and `since` carry a mandatory window
|
|
3
|
+
*
|
|
4
|
+
* Upstream's grammar makes `within` non-optional on all three:
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* once_op = { "formerly" ~ within ~ atom }
|
|
8
|
+
* previous_op = { "previous" ~ within ~ atom }
|
|
9
|
+
* conjunct_or_since = { neg_conjunct ~ ("since" ~ within ~ atom)? }
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* The typed builders make this unrepresentable — `formerly(window, body)` has
|
|
13
|
+
* nowhere to put a missing window — which is most of the fix. This check is
|
|
14
|
+
* the other part: `raw()` exists as an escape hatch, a hand-written `.dw` can
|
|
15
|
+
* be built by any other tool, and `chant audit` runs over files chant never
|
|
16
|
+
* emitted. A wall that only holds for the path that cannot breach it is not
|
|
17
|
+
* a wall.
|
|
18
|
+
*/
|
|
19
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
20
|
+
import { dogwoodPolicyFiles, scanWindowlessOperators } from "../../dogwood/scan";
|
|
21
|
+
|
|
22
|
+
export const dwdc012: PostSynthCheck = {
|
|
23
|
+
id: "DWDC012",
|
|
24
|
+
description: "A dogwood formerly/previous/since operator carries its mandatory within window",
|
|
25
|
+
|
|
26
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
27
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
28
|
+
|
|
29
|
+
for (const policies of dogwoodPolicyFiles(ctx)) {
|
|
30
|
+
const seen = new Set<string>();
|
|
31
|
+
for (const operator of scanWindowlessOperators(policies.text)) {
|
|
32
|
+
if (seen.has(operator)) continue;
|
|
33
|
+
seen.add(operator);
|
|
34
|
+
diagnostics.push({
|
|
35
|
+
checkId: "DWDC012",
|
|
36
|
+
severity: "error",
|
|
37
|
+
message: `Dogwood policy set "${policies.source}" uses \`${operator}\` with no \`within\` window. All three past-only operators require one — write \`${operator} within <n><s|m|h|d> …\`.`,
|
|
38
|
+
entity: policies.source,
|
|
39
|
+
lexicon: policies.lexicon,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return diagnostics;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DWDE010: the emitted `.dw` set validates clean under `dogwood validate`
|
|
3
|
+
*
|
|
4
|
+
* The CLI-gated half of the epic's validation split. Everything the DWDC walls
|
|
5
|
+
* ask is answerable in TypeScript and gates unconditionally; everything else —
|
|
6
|
+
* macro expansion, the temporal type checker, the Cedar body checked against
|
|
7
|
+
* the action schema *through upstream's own frontend* — needs upstream's Rust
|
|
8
|
+
* frontend, which ships as a binary and nothing else. No npm package, no wasm
|
|
9
|
+
* build, no bindings.
|
|
10
|
+
*
|
|
11
|
+
* So this check has two modes and says which one it is in:
|
|
12
|
+
*
|
|
13
|
+
* - **Binary present.** Runs `dogwood validate --format json` over each
|
|
14
|
+
* emitted policy set and reports every finding as an error. Byte-offset
|
|
15
|
+
* labels come through as byte offsets — see `../../dogwood/cli.ts` for why
|
|
16
|
+
* they are not converted to line/column.
|
|
17
|
+
* - **Binary absent.** Exactly one `info` finding naming the binary, where
|
|
18
|
+
* chant looked, and this issue. The epic's words are "an explicit,
|
|
19
|
+
* issue-linked exception, not a silent one" — a check that quietly passes
|
|
20
|
+
* when it could not run is claiming a guarantee it did not make, which is
|
|
21
|
+
* the same failure CEDE010's no-schema advisory exists to avoid.
|
|
22
|
+
*
|
|
23
|
+
* A run that could not be made at all — a spawn failure, an unknown flag, JSON
|
|
24
|
+
* in a shape this adapter does not know — is `warning`, not `error`. Upstream
|
|
25
|
+
* is a read-only squash-sync mirror with no tags and no changelog, and a flag
|
|
26
|
+
* rename in a sync would otherwise fail every build that has the binary
|
|
27
|
+
* installed. The adapter never reads exit 2 as "rejected" on its own for the
|
|
28
|
+
* same reason: clap spends that code on usage errors too.
|
|
29
|
+
*/
|
|
30
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
31
|
+
import {
|
|
32
|
+
DOGWOOD_BINARY_NAME,
|
|
33
|
+
DOGWOOD_SEARCH_ORDER,
|
|
34
|
+
findDogwoodBinary,
|
|
35
|
+
formatDogwoodDiagnostic,
|
|
36
|
+
runDogwoodValidate,
|
|
37
|
+
} from "../../dogwood/cli";
|
|
38
|
+
import { describeBundle, planDogwoodRuns } from "./dogwood-helpers";
|
|
39
|
+
|
|
40
|
+
export const dwde010: PostSynthCheck = {
|
|
41
|
+
id: "DWDE010",
|
|
42
|
+
description: "Emitted dogwood policy sets validate clean under `dogwood validate`, when the binary is available",
|
|
43
|
+
|
|
44
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
45
|
+
const plan = planDogwoodRuns(ctx);
|
|
46
|
+
if (!plan.hasPolicies) return [];
|
|
47
|
+
|
|
48
|
+
const binary = findDogwoodBinary();
|
|
49
|
+
if (!binary) {
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
checkId: "DWDE010",
|
|
53
|
+
severity: "info",
|
|
54
|
+
message: `This build emitted dogwood .dw policies, but no \`${DOGWOOD_BINARY_NAME}\` binary was found, so full .dw validation did not run — the DWDC walls checked what TypeScript can answer and nothing checked macro expansion, the temporal type check, or the Cedar body against the action schema. chant looked at ${DOGWOOD_SEARCH_ORDER}. See chant #1659.`,
|
|
55
|
+
lexicon: plan.lexicon,
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (plan.blocked) {
|
|
61
|
+
return [
|
|
62
|
+
{
|
|
63
|
+
checkId: "DWDE010",
|
|
64
|
+
severity: "info",
|
|
65
|
+
message: `This build emitted dogwood .dw policies and \`${DOGWOOD_BINARY_NAME}\` is available, but ${plan.blocked}, so full .dw validation did not run. See chant #1659.`,
|
|
66
|
+
lexicon: plan.lexicon,
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
72
|
+
|
|
73
|
+
for (const prepared of plan.bundles) {
|
|
74
|
+
const where = describeBundle(prepared);
|
|
75
|
+
const result = runDogwoodValidate(binary.path, prepared.bundle);
|
|
76
|
+
|
|
77
|
+
if (result.kind === "unusable") {
|
|
78
|
+
diagnostics.push({
|
|
79
|
+
checkId: "DWDE010",
|
|
80
|
+
severity: "warning",
|
|
81
|
+
message: `Dogwood policy set ${where} could not be validated — ${result.reason}. The policy set was neither accepted nor rejected.`,
|
|
82
|
+
entity: prepared.source,
|
|
83
|
+
lexicon: prepared.lexicon,
|
|
84
|
+
});
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (result.kind === "fatal") {
|
|
89
|
+
for (const finding of [result.error, ...result.related]) {
|
|
90
|
+
diagnostics.push({
|
|
91
|
+
checkId: "DWDE010",
|
|
92
|
+
severity: "error",
|
|
93
|
+
message: `Dogwood policy set ${where} could not be parsed or lowered: ${formatDogwoodDiagnostic(finding)}`,
|
|
94
|
+
entity: prepared.source,
|
|
95
|
+
lexicon: prepared.lexicon,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (result.kind === "rejected") {
|
|
102
|
+
for (const finding of result.errors) {
|
|
103
|
+
diagnostics.push({
|
|
104
|
+
checkId: "DWDE010",
|
|
105
|
+
severity: "error",
|
|
106
|
+
message: `Dogwood policy set ${where} fails \`dogwood validate\`: ${formatDogwoodDiagnostic(finding)}`,
|
|
107
|
+
entity: prepared.source,
|
|
108
|
+
lexicon: prepared.lexicon,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Upstream's own warnings ride along at warning severity. They do not
|
|
114
|
+
// fail `dogwood validate` (`passed` ignores them), and they should not
|
|
115
|
+
// fail a build either — but dropping them would throw away the half of
|
|
116
|
+
// the report that says a policy is legal and pointless.
|
|
117
|
+
for (const finding of result.warnings) {
|
|
118
|
+
diagnostics.push({
|
|
119
|
+
checkId: "DWDE010",
|
|
120
|
+
severity: "warning",
|
|
121
|
+
message: `Dogwood policy set ${where} draws a \`dogwood validate\` warning: ${formatDogwoodDiagnostic(finding)}`,
|
|
122
|
+
entity: prepared.source,
|
|
123
|
+
lexicon: prepared.lexicon,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return diagnostics;
|
|
129
|
+
},
|
|
130
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DWDE011: the lowered Cedar body validates clean under Cedar's own validator
|
|
3
|
+
*
|
|
4
|
+
* The epic's stated design for the non-temporal half: chant does not
|
|
5
|
+
* re-implement lowering — `dogwood lower` produces the analyzable plain-Cedar
|
|
6
|
+
* form, and a reimplementation would drift — but once that form exists, Cedar's
|
|
7
|
+
* own validator is the thing that judges it. The #1657 verification put all 86
|
|
8
|
+
* upstream example bundles through this exact path and every one parsed and
|
|
9
|
+
* validated clean in strict mode, which is what makes a finding here mean
|
|
10
|
+
* something: the lowered output is genuinely plain Cedar, so a validation error
|
|
11
|
+
* is the policy's, not the pipeline's.
|
|
12
|
+
*
|
|
13
|
+
* What this catches that DWDE010 does not is narrow but real. `dogwood
|
|
14
|
+
* validate` type-checks through upstream's vendored Cedar; this runs the
|
|
15
|
+
* *published* `@cedar-policy/cedar-wasm` over the same artifacts. A body that
|
|
16
|
+
* upstream's pinned Cedar accepts and the Cedar the rest of chant validates
|
|
17
|
+
* against rejects is exactly the drift the pin exists to make visible — and the
|
|
18
|
+
* augmented schema, with its hoisted `context.*` temporal slots, is the form a
|
|
19
|
+
* downstream Cedar policy store will actually receive.
|
|
20
|
+
*
|
|
21
|
+
* The cedar-wasm traps from #1648 all apply and are handled in
|
|
22
|
+
* `./wasm-helpers.ts`: `type: "success"` means validation *ran*, not that it
|
|
23
|
+
* passed; `validationErrors` comes back in a different order almost every call
|
|
24
|
+
* and is sorted before anything reads it; a malformed call throws rather than
|
|
25
|
+
* returning a failure answer.
|
|
26
|
+
*
|
|
27
|
+
* Silent when `lower` did not run — the binary is absent, or no action schema
|
|
28
|
+
* was emitted. Both cases are DWDE010's advisory to report, once, rather than
|
|
29
|
+
* twice in different words.
|
|
30
|
+
*/
|
|
31
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
32
|
+
import { findDogwoodBinary, runDogwoodLower } from "../../dogwood/cli";
|
|
33
|
+
import { describeBundle, planDogwoodRuns } from "./dogwood-helpers";
|
|
34
|
+
import { loadWasm, validatePolicySet } from "./wasm-helpers";
|
|
35
|
+
|
|
36
|
+
export const dwde011: PostSynthCheck = {
|
|
37
|
+
id: "DWDE011",
|
|
38
|
+
description: "The Cedar `dogwood lower` produces validates clean against the augmented schema (cedar-wasm validate)",
|
|
39
|
+
|
|
40
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
41
|
+
const plan = planDogwoodRuns(ctx);
|
|
42
|
+
if (!plan.hasPolicies || plan.blocked || plan.bundles.length === 0) return [];
|
|
43
|
+
|
|
44
|
+
const binary = findDogwoodBinary();
|
|
45
|
+
if (!binary) return [];
|
|
46
|
+
|
|
47
|
+
// A missing validator is not this check's finding to report; the cedar leg
|
|
48
|
+
// says so already through CEDC010.
|
|
49
|
+
const wasm = loadWasm();
|
|
50
|
+
if (!wasm) return [];
|
|
51
|
+
|
|
52
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
53
|
+
|
|
54
|
+
for (const prepared of plan.bundles) {
|
|
55
|
+
const where = describeBundle(prepared);
|
|
56
|
+
const lowered = runDogwoodLower(binary.path, prepared.bundle);
|
|
57
|
+
|
|
58
|
+
if (lowered.kind === "unusable") {
|
|
59
|
+
diagnostics.push({
|
|
60
|
+
checkId: "DWDE011",
|
|
61
|
+
severity: "warning",
|
|
62
|
+
message: `Dogwood policy set ${where} could not be lowered to Cedar — ${lowered.reason}. Its non-temporal body was not checked by Cedar's validator.`,
|
|
63
|
+
entity: prepared.source,
|
|
64
|
+
lexicon: prepared.lexicon,
|
|
65
|
+
});
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (lowered.kind === "fatal") {
|
|
70
|
+
// The same fatal DWDE010 reports from its own run. Saying it twice
|
|
71
|
+
// helps nobody, so this arm stays quiet and lets the validate leg own
|
|
72
|
+
// the parse/lower channel.
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const { cedarPolicies, cedarSchema } = lowered.value;
|
|
77
|
+
|
|
78
|
+
// The lowered policies go in as Cedar text. cedar-wasm synthesizes ids
|
|
79
|
+
// (`policy0`, `policy1`) for a bare-string set and does not read `@id`
|
|
80
|
+
// annotations as ids (#1648), so a finding names the .dw file it came
|
|
81
|
+
// from as well as the id the validator used.
|
|
82
|
+
const outcome = validatePolicySet(wasm, { staticPolicies: cedarPolicies }, cedarSchema);
|
|
83
|
+
|
|
84
|
+
if (outcome.failure) {
|
|
85
|
+
diagnostics.push({
|
|
86
|
+
checkId: "DWDE011",
|
|
87
|
+
severity: "error",
|
|
88
|
+
message: `The Cedar lowered from dogwood policy set ${where} could not be validated: ${outcome.failure}`,
|
|
89
|
+
entity: prepared.source,
|
|
90
|
+
lexicon: prepared.lexicon,
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const finding of outcome.errors) {
|
|
96
|
+
diagnostics.push({
|
|
97
|
+
checkId: "DWDE011",
|
|
98
|
+
severity: "error",
|
|
99
|
+
message: `The Cedar lowered from dogwood policy set ${where} fails Cedar validation (lowered policy "${finding.policyId}"): ${finding.message}`,
|
|
100
|
+
entity: prepared.source,
|
|
101
|
+
lexicon: prepared.lexicon,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return diagnostics;
|
|
107
|
+
},
|
|
108
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DWDS010: an emitted event schema with no pinned field widens every predicate
|
|
3
|
+
*
|
|
4
|
+
* The #1657 verification is specific about this one. `ServiceSchema::defaults()`
|
|
5
|
+
* — what runs when nobody passes `--event-schema` — uses
|
|
6
|
+
* `configuration/event-schemas/pinned.dwschema`, which carries
|
|
7
|
+
* `pin callerPrincipal: principalType(A) = principal` on every kind. Every
|
|
8
|
+
* temporal predicate is therefore correlated to the deciding request's
|
|
9
|
+
* principal, and events from other principals are invisible.
|
|
10
|
+
*
|
|
11
|
+
* Supplying any event schema opts out of that default wholesale. So a schema
|
|
12
|
+
* emitted without a pin does not merely "not add" a correlation: it removes
|
|
13
|
+
* one the policy author very likely assumed, and every `formerly` in the set
|
|
14
|
+
* starts matching other principals' events. That is a legitimate design —
|
|
15
|
+
* cross-principal correlation is the reason to write your own schema — but it
|
|
16
|
+
* is a decision, and a decision nobody can see in a diff is one nobody made.
|
|
17
|
+
*
|
|
18
|
+
* Report-only: chant does not know which the author wanted, only that it
|
|
19
|
+
* should be said out loud. `defaultEventSchema({ pinCallerPrincipal: false })`
|
|
20
|
+
* also stamps the reasoning into the emitted file.
|
|
21
|
+
*/
|
|
22
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
23
|
+
import { dogwoodSchemaFiles, readEventSchema } from "../../dogwood/scan";
|
|
24
|
+
|
|
25
|
+
export const dwds010: PostSynthCheck = {
|
|
26
|
+
id: "DWDS010",
|
|
27
|
+
description: "An emitted dogwood event schema pins its temporal predicates to a request-side value",
|
|
28
|
+
|
|
29
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
30
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
31
|
+
|
|
32
|
+
for (const schema of dogwoodSchemaFiles(ctx)) {
|
|
33
|
+
const facts = readEventSchema(schema.text);
|
|
34
|
+
if (facts.kinds.length === 0 || facts.hasPin) continue;
|
|
35
|
+
diagnostics.push({
|
|
36
|
+
checkId: "DWDS010",
|
|
37
|
+
severity: "warning",
|
|
38
|
+
message: `Dogwood event schema "${schema.source}" declares no pinned field, so temporal predicates correlate across every principal — wider than upstream's default, which pins callerPrincipal to the deciding request's principal. Add a pinned field, or record why cross-principal correlation is wanted.`,
|
|
39
|
+
entity: schema.source,
|
|
40
|
+
lexicon: schema.lexicon,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return diagnostics;
|
|
45
|
+
},
|
|
46
|
+
};
|
package/dist/serializer.d.ts
CHANGED
|
@@ -19,6 +19,12 @@
|
|
|
19
19
|
* (#1650) generates typed classes onto exactly this shape, so nothing here
|
|
20
20
|
* changes when it lands — the guards become typed expressions rather than
|
|
21
21
|
* the opaque Cedar-expression strings they are today.
|
|
22
|
+
*
|
|
23
|
+
* A third view arrives when the build holds dogwood entities (#1658): `.dw`
|
|
24
|
+
* policy text and its `.dwschema`/`macros.dw` companions, rendered by
|
|
25
|
+
* `./dogwood/serialize.ts` and returned in the same result. Both legs share
|
|
26
|
+
* `./policy-text.ts` rather than a copy of it, because a `.dw` policy's head
|
|
27
|
+
* *is* a Cedar policy's head.
|
|
22
28
|
*/
|
|
23
29
|
import type { Declarable } from "@intentius/chant/declarable";
|
|
24
30
|
import type { Serializer } from "@intentius/chant/serializer";
|
|
@@ -69,14 +75,7 @@ export interface CedarPolicyProps {
|
|
|
69
75
|
*/
|
|
70
76
|
annotations?: Record<string, string>;
|
|
71
77
|
}
|
|
72
|
-
|
|
73
|
-
export declare function escapeCedarString(value: string): string;
|
|
74
|
-
/**
|
|
75
|
-
* Derive a policy id from a logical name: `allowAdminRead` → `allow-admin-read`.
|
|
76
|
-
* Cedar ids are free-form strings; kebab-case keeps them readable in the
|
|
77
|
-
* `@id` annotation and stable across a rename-free refactor.
|
|
78
|
-
*/
|
|
79
|
-
export declare function policyIdFromLogicalName(name: string): string;
|
|
78
|
+
export { conditionStrings, escapeCedarString, getProps, isRecord, policyIdFromLogicalName, renderPolicyHead, renderScope, resolvePolicyId, } from "./policy-text.js";
|
|
80
79
|
/**
|
|
81
80
|
* One policy, as `.cedar` text.
|
|
82
81
|
*
|
|
@@ -84,6 +83,9 @@ export declare function policyIdFromLogicalName(name: string): string;
|
|
|
84
83
|
* nothing else — `AWS::VerifiedPermissions::Policy` carries its policy as
|
|
85
84
|
* `Definition.Static.Statement`, a single Cedar policy rather than a set. Two
|
|
86
85
|
* renderers would be two dialects; there is one.
|
|
86
|
+
*
|
|
87
|
+
* The head comes from `renderPolicyHead`, which the dogwood dialect's `.dw`
|
|
88
|
+
* leg also calls: three surfaces, one rendering of an annotation and a scope.
|
|
87
89
|
*/
|
|
88
90
|
export declare function renderPolicyText(id: string, props: Record<string, unknown>): string;
|
|
89
91
|
/** The JSON policy-set envelope, exactly as `cedar-wasm` accepts it. */
|
|
@@ -117,16 +119,6 @@ export interface CedarPolicyRecord {
|
|
|
117
119
|
/** Props with references resolved, ready for either renderer. */
|
|
118
120
|
props: Record<string, unknown>;
|
|
119
121
|
}
|
|
120
|
-
/**
|
|
121
|
-
* The Cedar id for a policy: an explicit `annotations.id` when the author gave
|
|
122
|
-
* one, else derived from the logical name.
|
|
123
|
-
*
|
|
124
|
-
* This is the only rule that links a chant entity to a policy in a live AVP
|
|
125
|
-
* store (#1652) — the observation resolves the same id from the same props and
|
|
126
|
-
* matches it against the `@id` annotation the statement carries. Two copies of
|
|
127
|
-
* this rule would be a mapping that drifts silently, so there is one.
|
|
128
|
-
*/
|
|
129
|
-
export declare function resolvePolicyId(logicalName: string, props: Record<string, unknown>): string;
|
|
130
122
|
/**
|
|
131
123
|
* Every `Cedar::Policy` in a build, with references walked and ids resolved.
|
|
132
124
|
*
|
package/dist/serializer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,6BAA6B,CAAC;AAGhF,OAAO,EAAgD,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAY5F,8CAA8C;AAC9C,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAEzD,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAClB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GACrB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAA;CAAE,GACtC;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAA;CAAE,GACjD;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAEvD,+CAA+C;AAC/C,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,qDAAqD;IACrD,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,qDAAqD;IACrD,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AASD,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,uBAAuB,EACvB,gBAAgB,EAChB,WAAW,EACX,eAAe,GAChB,MAAM,eAAe,CAAC;AAwBvB;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAWnF;AAID,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACtC,aAAa,EAAE,OAAO,EAAE,CAAC;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,GAAG,CAAC,EAAE,kBAAkB,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA4BxF;AAID,yFAAyF;AACzF,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,iBAAiB,EAAE,CAiBzF;AAID,eAAO,MAAM,eAAe,EAAE,UAqD7B,CAAC"}
|