@intentius/chant-lexicon-cedar 0.44.8
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 +190 -0
- package/dist/avp/ambient.d.ts +54 -0
- package/dist/avp/ambient.d.ts.map +1 -0
- package/dist/avp/client.d.ts +127 -0
- package/dist/avp/client.d.ts.map +1 -0
- package/dist/avp/describe-resources.d.ts +42 -0
- package/dist/avp/describe-resources.d.ts.map +1 -0
- package/dist/avp/embed.d.ts +120 -0
- package/dist/avp/embed.d.ts.map +1 -0
- package/dist/avp/live-export.d.ts +94 -0
- package/dist/avp/live-export.d.ts.map +1 -0
- package/dist/avp/ownership.d.ts +97 -0
- package/dist/avp/ownership.d.ts.map +1 -0
- package/dist/avp/statement.d.ts +22 -0
- package/dist/avp/statement.d.ts.map +1 -0
- package/dist/avp/store.d.ts +82 -0
- package/dist/avp/store.d.ts.map +1 -0
- package/dist/avp/testdata/mock-transport.d.ts +55 -0
- package/dist/avp/testdata/mock-transport.d.ts.map +1 -0
- package/dist/codegen/docs-cli.d.ts +3 -0
- package/dist/codegen/docs-cli.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts +26 -0
- package/dist/codegen/docs.d.ts.map +1 -0
- package/dist/codegen/emit.d.ts +85 -0
- package/dist/codegen/emit.d.ts.map +1 -0
- package/dist/codegen/generate-cli.d.ts +3 -0
- package/dist/codegen/generate-cli.d.ts.map +1 -0
- package/dist/codegen/generate.d.ts +37 -0
- package/dist/codegen/generate.d.ts.map +1 -0
- package/dist/codegen/naming.d.ts +48 -0
- package/dist/codegen/naming.d.ts.map +1 -0
- package/dist/codegen/package.d.ts +10 -0
- package/dist/codegen/package.d.ts.map +1 -0
- package/dist/composites/deny-by-default-set.d.ts +58 -0
- package/dist/composites/deny-by-default-set.d.ts.map +1 -0
- package/dist/composites/index.d.ts +12 -0
- package/dist/composites/index.d.ts.map +1 -0
- package/dist/composites/owner-can-manage.d.ts +48 -0
- package/dist/composites/owner-can-manage.d.ts.map +1 -0
- package/dist/config.d.ts +102 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/coverage.d.ts +59 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/detect.d.ts +18 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/generated/index.d.ts +251 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/runtime.d.ts +2 -0
- package/dist/generated/runtime.d.ts.map +1 -0
- package/dist/import/adapter.d.ts +21 -0
- package/dist/import/adapter.d.ts.map +1 -0
- package/dist/import/clause-text.d.ts +38 -0
- package/dist/import/clause-text.d.ts.map +1 -0
- package/dist/import/generator.d.ts +53 -0
- package/dist/import/generator.d.ts.map +1 -0
- package/dist/import/parser.d.ts +57 -0
- package/dist/import/parser.d.ts.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/init-templates.d.ts +32 -0
- package/dist/init-templates.d.ts.map +1 -0
- package/dist/integrity.json +22 -0
- package/dist/lint/audit-catalog.d.ts +29 -0
- package/dist/lint/audit-catalog.d.ts.map +1 -0
- package/dist/lint/post-synth/cedar-helpers.d.ts +97 -0
- package/dist/lint/post-synth/cedar-helpers.d.ts.map +1 -0
- package/dist/lint/post-synth/cedc010.d.ts +17 -0
- package/dist/lint/post-synth/cedc010.d.ts.map +1 -0
- package/dist/lint/post-synth/cedc011.d.ts +17 -0
- package/dist/lint/post-synth/cedc011.d.ts.map +1 -0
- package/dist/lint/post-synth/cedc012.d.ts +21 -0
- package/dist/lint/post-synth/cedc012.d.ts.map +1 -0
- package/dist/lint/post-synth/cedc013.d.ts +20 -0
- package/dist/lint/post-synth/cedc013.d.ts.map +1 -0
- package/dist/lint/post-synth/cedc014.d.ts +19 -0
- package/dist/lint/post-synth/cedc014.d.ts.map +1 -0
- package/dist/lint/post-synth/cede010.d.ts +34 -0
- package/dist/lint/post-synth/cede010.d.ts.map +1 -0
- package/dist/lint/post-synth/cede011.d.ts +22 -0
- package/dist/lint/post-synth/cede011.d.ts.map +1 -0
- package/dist/lint/post-synth/ceds010.d.ts +24 -0
- package/dist/lint/post-synth/ceds010.d.ts.map +1 -0
- package/dist/lint/post-synth/ceds011.d.ts +19 -0
- package/dist/lint/post-synth/ceds011.d.ts.map +1 -0
- package/dist/lint/post-synth/ceds012.d.ts +17 -0
- package/dist/lint/post-synth/ceds012.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts +3 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -0
- package/dist/lint/post-synth/wasm-helpers.d.ts +108 -0
- package/dist/lint/post-synth/wasm-helpers.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +10 -0
- package/dist/lint/rules/index.d.ts.map +1 -0
- package/dist/lint/rules/policy-shape.d.ts +31 -0
- package/dist/lint/rules/policy-shape.d.ts.map +1 -0
- package/dist/lsp/completions.d.ts +24 -0
- package/dist/lsp/completions.d.ts.map +1 -0
- package/dist/lsp/hover.d.ts +11 -0
- package/dist/lsp/hover.d.ts.map +1 -0
- package/dist/lsp/registry.d.ts +45 -0
- package/dist/lsp/registry.d.ts.map +1 -0
- package/dist/manifest.json +8 -0
- package/dist/mcp/index.d.ts +34 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/policy-coverage.d.ts +81 -0
- package/dist/mcp/policy-coverage.d.ts.map +1 -0
- package/dist/meta.json +699 -0
- package/dist/okf/index.md +37 -0
- package/dist/okf/rules/CEDC001.md +11 -0
- package/dist/okf/rules/CEDC010.md +11 -0
- package/dist/okf/rules/CEDC011.md +15 -0
- package/dist/okf/rules/CEDC012.md +11 -0
- package/dist/okf/rules/CEDC013.md +15 -0
- package/dist/okf/rules/CEDC014.md +15 -0
- package/dist/okf/rules/CEDE010.md +15 -0
- package/dist/okf/rules/CEDE011.md +15 -0
- package/dist/okf/rules/CEDS010.md +15 -0
- package/dist/okf/rules/CEDS011.md +11 -0
- package/dist/okf/rules/CEDS012.md +15 -0
- package/dist/okf/types/AdminAction.md +14 -0
- package/dist/okf/types/Application.md +14 -0
- package/dist/okf/types/ApproveAction.md +13 -0
- package/dist/okf/types/CommentAction.md +14 -0
- package/dist/okf/types/CreateAction.md +14 -0
- package/dist/okf/types/DeleteAction.md +14 -0
- package/dist/okf/types/Document.md +18 -0
- package/dist/okf/types/Folder.md +15 -0
- package/dist/okf/types/Group.md +14 -0
- package/dist/okf/types/ListAction.md +14 -0
- package/dist/okf/types/Policy.md +29 -0
- package/dist/okf/types/ReadAction.md +14 -0
- package/dist/okf/types/ServiceAccount.md +15 -0
- package/dist/okf/types/ShareAction.md +14 -0
- package/dist/okf/types/Team.md +14 -0
- package/dist/okf/types/User.md +18 -0
- package/dist/okf/types/WriteAction.md +14 -0
- package/dist/package-cli.d.ts +3 -0
- package/dist/package-cli.d.ts.map +1 -0
- package/dist/plugin.d.ts +8 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/rules/cedar-helpers.ts +209 -0
- package/dist/rules/cedc010.ts +80 -0
- package/dist/rules/cedc011.ts +47 -0
- package/dist/rules/cedc012.ts +64 -0
- package/dist/rules/cedc013.ts +68 -0
- package/dist/rules/cedc014.ts +73 -0
- package/dist/rules/cede010.ts +89 -0
- package/dist/rules/cede011.ts +58 -0
- package/dist/rules/ceds010.ts +57 -0
- package/dist/rules/ceds011.ts +42 -0
- package/dist/rules/ceds012.ts +50 -0
- package/dist/rules/policy-shape.ts +148 -0
- package/dist/rules/wasm-helpers.ts +315 -0
- package/dist/serializer.d.ts +138 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/spec/fetch.d.ts +71 -0
- package/dist/spec/fetch.d.ts.map +1 -0
- package/dist/spec/parse.d.ts +113 -0
- package/dist/spec/parse.d.ts.map +1 -0
- package/dist/spec/pin.d.ts +116 -0
- package/dist/spec/pin.d.ts.map +1 -0
- package/dist/spec/pinned-names.json +18 -0
- package/dist/spec/wasm.d.ts +143 -0
- package/dist/spec/wasm.d.ts.map +1 -0
- package/dist/types/index.d.ts +219 -0
- package/dist/validate-cli.d.ts +3 -0
- package/dist/validate-cli.d.ts.map +1 -0
- package/dist/validate.d.ts +25 -0
- package/dist/validate.d.ts.map +1 -0
- package/package.json +75 -0
- package/src/avp/OWNERSHIP.md +125 -0
- package/src/avp/ambient.test.ts +113 -0
- package/src/avp/ambient.ts +124 -0
- package/src/avp/client.ts +310 -0
- package/src/avp/describe-resources.test.ts +316 -0
- package/src/avp/describe-resources.ts +215 -0
- package/src/avp/embed.test.ts +114 -0
- package/src/avp/embed.ts +190 -0
- package/src/avp/live-export.test.ts +232 -0
- package/src/avp/live-export.ts +185 -0
- package/src/avp/ownership.test.ts +101 -0
- package/src/avp/ownership.ts +170 -0
- package/src/avp/statement.ts +50 -0
- package/src/avp/store.ts +152 -0
- package/src/avp/testdata/mock-transport.ts +147 -0
- package/src/codegen/docs-cli.ts +7 -0
- package/src/codegen/docs.ts +873 -0
- package/src/codegen/emit.ts +496 -0
- package/src/codegen/generate-cli.ts +18 -0
- package/src/codegen/generate.test.ts +128 -0
- package/src/codegen/generate.ts +123 -0
- package/src/codegen/naming.ts +101 -0
- package/src/codegen/package.ts +52 -0
- package/src/composites/composites.test.ts +206 -0
- package/src/composites/deny-by-default-set.ts +98 -0
- package/src/composites/index.ts +13 -0
- package/src/composites/owner-can-manage.ts +80 -0
- package/src/config-namespace.test.ts +44 -0
- package/src/config.test.ts +82 -0
- package/src/config.ts +119 -0
- package/src/coverage.test.ts +61 -0
- package/src/coverage.ts +166 -0
- package/src/detect.test.ts +64 -0
- package/src/detect.ts +59 -0
- package/src/generated/index.d.ts +219 -0
- package/src/generated/index.ts +279 -0
- package/src/generated/lexicon-cedar.json +699 -0
- package/src/generated/runtime.ts +2 -0
- package/src/import/adapter.ts +63 -0
- package/src/import/clause-text.ts +178 -0
- package/src/import/generator.test.ts +133 -0
- package/src/import/generator.ts +219 -0
- package/src/import/parser.test.ts +265 -0
- package/src/import/parser.ts +352 -0
- package/src/import/roundtrip.test.ts +127 -0
- package/src/import/testdata/full.cedar +36 -0
- package/src/import/testdata/full.cedar.json +238 -0
- package/src/import/testdata/realistic.cedar +35 -0
- package/src/import/testdata/simple.cedar +6 -0
- package/src/index.ts +94 -0
- package/src/init-templates.test.ts +158 -0
- package/src/init-templates.ts +358 -0
- package/src/lint/audit-catalog.ts +130 -0
- package/src/lint/post-synth/cedar-helpers.ts +209 -0
- package/src/lint/post-synth/cedc010.ts +80 -0
- package/src/lint/post-synth/cedc011.ts +47 -0
- package/src/lint/post-synth/cedc012.ts +64 -0
- package/src/lint/post-synth/cedc013.ts +68 -0
- package/src/lint/post-synth/cedc014.ts +73 -0
- package/src/lint/post-synth/cede010.ts +89 -0
- package/src/lint/post-synth/cede011.ts +58 -0
- package/src/lint/post-synth/ceds010.ts +57 -0
- package/src/lint/post-synth/ceds011.ts +42 -0
- package/src/lint/post-synth/ceds012.ts +50 -0
- package/src/lint/post-synth/index.ts +25 -0
- package/src/lint/post-synth/post-synth.test.ts +474 -0
- package/src/lint/post-synth/wasm-helpers.ts +315 -0
- package/src/lint/rules/index.ts +12 -0
- package/src/lint/rules/policy-shape.test.ts +90 -0
- package/src/lint/rules/policy-shape.ts +148 -0
- package/src/lsp/completions.test.ts +111 -0
- package/src/lsp/completions.ts +55 -0
- package/src/lsp/hover.test.ts +82 -0
- package/src/lsp/hover.ts +75 -0
- package/src/lsp/registry.ts +74 -0
- package/src/mcp/index.ts +103 -0
- package/src/mcp/policy-coverage.test.ts +176 -0
- package/src/mcp/policy-coverage.ts +205 -0
- package/src/package-cli.ts +22 -0
- package/src/plugin.test.ts +115 -0
- package/src/plugin.ts +312 -0
- package/src/serializer.test.ts +502 -0
- package/src/serializer.ts +335 -0
- package/src/skills/chant-cedar-authoring.md +180 -0
- package/src/skills/chant-cedar-avp-embedding.md +125 -0
- package/src/skills/chant-cedar-meta-policy.md +119 -0
- package/src/spec/default-schema.cedarschema +108 -0
- package/src/spec/fetch.ts +107 -0
- package/src/spec/parse.test.ts +123 -0
- package/src/spec/parse.ts +253 -0
- package/src/spec/pin.test.ts +88 -0
- package/src/spec/pin.ts +243 -0
- package/src/spec/pinned-names.json +18 -0
- package/src/spec/wasm.ts +283 -0
- package/src/validate-cli.ts +8 -0
- package/src/validate.ts +85 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// Code generated by chant generate. DO NOT EDIT.
|
|
2
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Entity UID of `App::Application`.
|
|
6
|
+
*/
|
|
7
|
+
export type ApplicationUid = `App::Application::"${string}"`;
|
|
8
|
+
|
|
9
|
+
export interface ApplicationAttributesProps {
|
|
10
|
+
name: string;
|
|
11
|
+
owner: TeamUid;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Entity UID of `App::Document`.
|
|
16
|
+
* Member of: App::Folder.
|
|
17
|
+
*/
|
|
18
|
+
export type DocumentUid = `App::Document::"${string}"`;
|
|
19
|
+
|
|
20
|
+
export interface DocumentAttributesProps {
|
|
21
|
+
classification: string;
|
|
22
|
+
folder: FolderUid;
|
|
23
|
+
owner: UserUid;
|
|
24
|
+
public: boolean;
|
|
25
|
+
tags: string[];
|
|
26
|
+
title: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Entity UID of `App::Folder`.
|
|
31
|
+
*/
|
|
32
|
+
export type FolderUid = `App::Folder::"${string}"`;
|
|
33
|
+
|
|
34
|
+
export interface FolderAttributesProps {
|
|
35
|
+
name: string;
|
|
36
|
+
owner: UserUid;
|
|
37
|
+
tags: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Entity UID of `App::Group`.
|
|
42
|
+
*/
|
|
43
|
+
export type GroupUid = `App::Group::"${string}"`;
|
|
44
|
+
|
|
45
|
+
export interface GroupAttributesProps {
|
|
46
|
+
description?: string;
|
|
47
|
+
name: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Entity UID of `App::ServiceAccount`.
|
|
52
|
+
* Member of: App::Group.
|
|
53
|
+
*/
|
|
54
|
+
export type ServiceAccountUid = `App::ServiceAccount::"${string}"`;
|
|
55
|
+
|
|
56
|
+
export interface ServiceAccountAttributesProps {
|
|
57
|
+
name: string;
|
|
58
|
+
owner: UserUid;
|
|
59
|
+
rotatedAt?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Entity UID of `App::Team`.
|
|
64
|
+
* Member of: App::Group.
|
|
65
|
+
*/
|
|
66
|
+
export type TeamUid = `App::Team::"${string}"`;
|
|
67
|
+
|
|
68
|
+
export interface TeamAttributesProps {
|
|
69
|
+
costCenter?: string;
|
|
70
|
+
name: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Entity UID of `App::User`.
|
|
75
|
+
* Member of: App::Group, App::Team.
|
|
76
|
+
*/
|
|
77
|
+
export type UserUid = `App::User::"${string}"`;
|
|
78
|
+
|
|
79
|
+
export interface UserAttributesProps {
|
|
80
|
+
department: string;
|
|
81
|
+
email: string;
|
|
82
|
+
level: number;
|
|
83
|
+
manager?: UserUid;
|
|
84
|
+
mfaEnabled: boolean;
|
|
85
|
+
roles: string[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface AdminContextProps {
|
|
89
|
+
breakGlass?: boolean;
|
|
90
|
+
mfa: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface ApproveContextProps {
|
|
94
|
+
mfa: boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface CommentContextProps {
|
|
98
|
+
mfa: boolean;
|
|
99
|
+
reason?: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface CreateContextProps {
|
|
103
|
+
mfa: boolean;
|
|
104
|
+
reason?: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface DeleteContextProps {
|
|
108
|
+
approvedBy?: UserUid;
|
|
109
|
+
mfa: boolean;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ListContextProps {
|
|
113
|
+
mfa: boolean;
|
|
114
|
+
sourceIp?: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface ReadContextProps {
|
|
118
|
+
mfa: boolean;
|
|
119
|
+
sourceIp?: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface ShareContextProps {
|
|
123
|
+
approvedBy?: UserUid;
|
|
124
|
+
mfa: boolean;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface WriteContextProps {
|
|
128
|
+
mfa: boolean;
|
|
129
|
+
reason?: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Every entity type name the schema declares, as Cedar writes it. */
|
|
133
|
+
export type EntityTypeName = "App::Application" | "App::Document" | "App::Folder" | "App::Group" | "App::ServiceAccount" | "App::Team" | "App::User";
|
|
134
|
+
|
|
135
|
+
/** Every entity UID the schema can produce. */
|
|
136
|
+
export type EntityUid = ApplicationUid | DocumentUid | FolderUid | GroupUid | ServiceAccountUid | TeamUid | UserUid;
|
|
137
|
+
|
|
138
|
+
/** Every action UID the schema declares. */
|
|
139
|
+
export type ActionUid = "App::Action::\"admin\"" | "App::Action::\"approve\"" | "App::Action::\"comment\"" | "App::Action::\"create\"" | "App::Action::\"delete\"" | "App::Action::\"list\"" | "App::Action::\"read\"" | "App::Action::\"share\"" | "App::Action::\"write\"";
|
|
140
|
+
|
|
141
|
+
/** Anything a policy scope may name. */
|
|
142
|
+
export type PolicyRef = EntityUid | ActionUid;
|
|
143
|
+
|
|
144
|
+
/** A Cedar policy either permits or forbids; there is no third effect. */
|
|
145
|
+
export type PolicyEffect = "permit" | "forbid";
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* One scope position (`principal`, `action`, `resource`).
|
|
149
|
+
*
|
|
150
|
+
* `{}` is the unconstrained form Cedar writes as a bare variable. The
|
|
151
|
+
* constrained forms mirror the grammar: `== E`, `in E`, `in [E, …]`,
|
|
152
|
+
* `is T`, and `is T in E`.
|
|
153
|
+
*/
|
|
154
|
+
export type PolicyScope =
|
|
155
|
+
| Record<string, never>
|
|
156
|
+
| { eq: PolicyRef; in?: never; is?: never }
|
|
157
|
+
| { in: PolicyRef | PolicyRef[]; eq?: never; is?: never }
|
|
158
|
+
| { is: EntityTypeName; in?: PolicyRef | PolicyRef[]; eq?: never };
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The props of a `Cedar::Policy`.
|
|
162
|
+
*
|
|
163
|
+
* `when`/`unless` carry Cedar expression text. They stay strings because
|
|
164
|
+
* Cedar's expression grammar is the policy language itself — typing it in
|
|
165
|
+
* TypeScript is import/reconcile's problem (epic #1645 sub-issue 6), not
|
|
166
|
+
* codegen's.
|
|
167
|
+
*/
|
|
168
|
+
export interface PolicyProps {
|
|
169
|
+
/** Defaults to `permit` when omitted. */
|
|
170
|
+
effect?: PolicyEffect;
|
|
171
|
+
principal?: PolicyScope;
|
|
172
|
+
action?: PolicyScope;
|
|
173
|
+
resource?: PolicyScope;
|
|
174
|
+
/** Cedar expression strings, each emitted as its own `when { … }` clause. */
|
|
175
|
+
when?: string[];
|
|
176
|
+
/** Cedar expression strings, each emitted as its own `unless { … }` clause. */
|
|
177
|
+
unless?: string[];
|
|
178
|
+
/** Emitted as `@key("value")`; an explicit `id` wins over the logical name. */
|
|
179
|
+
annotations?: Record<string, string>;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export declare const Policy: new (props: PolicyProps) => Declarable;
|
|
183
|
+
|
|
184
|
+
export declare const Application: new (props: ApplicationAttributesProps) => Declarable;
|
|
185
|
+
export declare const ApplicationAttributes: new (props: ApplicationAttributesProps) => Declarable;
|
|
186
|
+
export declare const Document: new (props: DocumentAttributesProps) => Declarable;
|
|
187
|
+
export declare const DocumentAttributes: new (props: DocumentAttributesProps) => Declarable;
|
|
188
|
+
export declare const Folder: new (props: FolderAttributesProps) => Declarable;
|
|
189
|
+
export declare const FolderAttributes: new (props: FolderAttributesProps) => Declarable;
|
|
190
|
+
export declare const Group: new (props: GroupAttributesProps) => Declarable;
|
|
191
|
+
export declare const GroupAttributes: new (props: GroupAttributesProps) => Declarable;
|
|
192
|
+
export declare const ServiceAccount: new (props: ServiceAccountAttributesProps) => Declarable;
|
|
193
|
+
export declare const ServiceAccountAttributes: new (props: ServiceAccountAttributesProps) => Declarable;
|
|
194
|
+
export declare const Team: new (props: TeamAttributesProps) => Declarable;
|
|
195
|
+
export declare const TeamAttributes: new (props: TeamAttributesProps) => Declarable;
|
|
196
|
+
export declare const User: new (props: UserAttributesProps) => Declarable;
|
|
197
|
+
export declare const UserAttributes: new (props: UserAttributesProps) => Declarable;
|
|
198
|
+
|
|
199
|
+
export declare const AdminAction: "App::Action::\"admin\"";
|
|
200
|
+
export declare const AdminContext: new (props: AdminContextProps) => Declarable;
|
|
201
|
+
export declare const ApproveAction: "App::Action::\"approve\"";
|
|
202
|
+
export declare const ApproveContext: new (props: ApproveContextProps) => Declarable;
|
|
203
|
+
export declare const CommentAction: "App::Action::\"comment\"";
|
|
204
|
+
export declare const CommentContext: new (props: CommentContextProps) => Declarable;
|
|
205
|
+
export declare const CreateAction: "App::Action::\"create\"";
|
|
206
|
+
export declare const CreateContext: new (props: CreateContextProps) => Declarable;
|
|
207
|
+
export declare const DeleteAction: "App::Action::\"delete\"";
|
|
208
|
+
export declare const DeleteContext: new (props: DeleteContextProps) => Declarable;
|
|
209
|
+
export declare const ListAction: "App::Action::\"list\"";
|
|
210
|
+
export declare const ListContext: new (props: ListContextProps) => Declarable;
|
|
211
|
+
export declare const ReadAction: "App::Action::\"read\"";
|
|
212
|
+
export declare const ReadContext: new (props: ReadContextProps) => Declarable;
|
|
213
|
+
export declare const ShareAction: "App::Action::\"share\"";
|
|
214
|
+
export declare const ShareContext: new (props: ShareContextProps) => Declarable;
|
|
215
|
+
export declare const WriteAction: "App::Action::\"write\"";
|
|
216
|
+
export declare const WriteContext: new (props: WriteContextProps) => Declarable;
|
|
217
|
+
|
|
218
|
+
export declare const ALL_ACTIONS: readonly ActionUid[];
|
|
219
|
+
export declare const ALL_ENTITY_TYPES: readonly EntityTypeName[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-cli.d.ts","sourceRoot":"","sources":["../src/validate-cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate the generated lexicon-cedar artifacts.
|
|
3
|
+
*
|
|
4
|
+
* The required-name list below is the surface this package publishes when it
|
|
5
|
+
* generates from its own bundled default schema, which is what `prepack`,
|
|
6
|
+
* `just check-lexicons` and CI all do. It is not a claim about a user's
|
|
7
|
+
* project schema — theirs produces their own names, and `coverage()` is the
|
|
8
|
+
* check that answers whether their schema is fully reachable.
|
|
9
|
+
*
|
|
10
|
+
* Listing them explicitly is the point: `src/generated/` is gitignored, so
|
|
11
|
+
* without this list nothing in the repository records what the default schema
|
|
12
|
+
* is supposed to produce, and a codegen change that quietly dropped every
|
|
13
|
+
* action would still pass "the registry parses".
|
|
14
|
+
*/
|
|
15
|
+
import { type ValidateResult } from "@intentius/chant/codegen/validate";
|
|
16
|
+
export type { ValidateCheck, ValidateResult } from "@intentius/chant/codegen/validate";
|
|
17
|
+
/** Exposed so tests can assert the list stays above the tier-3 bar. */
|
|
18
|
+
export declare const requiredNames: readonly string[];
|
|
19
|
+
/**
|
|
20
|
+
* Validate the generated lexicon-cedar artifacts.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validate(opts?: {
|
|
23
|
+
basePath?: string;
|
|
24
|
+
}): Promise<ValidateResult>;
|
|
25
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAElG,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAmDvF,uEAAuE;AACvE,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAAmB,CAAC;AAE/D;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAQpF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@intentius/chant-lexicon-cedar",
|
|
3
|
+
"version": "0.44.8",
|
|
4
|
+
"description": "Cedar lexicon for chant — typed authoring for Cedar authorization policies",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"homepage": "https://intentius.io/chant",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/INTENTIUS/chant.git",
|
|
10
|
+
"directory": "lexicons/cedar"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/INTENTIUS/chant/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"infrastructure-as-code",
|
|
17
|
+
"iac",
|
|
18
|
+
"typescript",
|
|
19
|
+
"cedar",
|
|
20
|
+
"authorization",
|
|
21
|
+
"policy",
|
|
22
|
+
"chant"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"files": [
|
|
26
|
+
"src/",
|
|
27
|
+
"dist/"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"development": "./src/index.ts",
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"default": "./src/index.ts"
|
|
37
|
+
},
|
|
38
|
+
"./*": {
|
|
39
|
+
"development": "./src/*.ts",
|
|
40
|
+
"types": "./dist/*.d.ts",
|
|
41
|
+
"default": "./src/*.ts"
|
|
42
|
+
},
|
|
43
|
+
"./lint/post-synth": {
|
|
44
|
+
"development": "./src/lint/post-synth/index.ts",
|
|
45
|
+
"types": "./dist/lint/post-synth/index.d.ts",
|
|
46
|
+
"default": "./src/lint/post-synth/index.ts"
|
|
47
|
+
},
|
|
48
|
+
"./manifest": "./dist/manifest.json",
|
|
49
|
+
"./meta": "./dist/meta.json",
|
|
50
|
+
"./types": "./dist/types/index.d.ts"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"generate": "tsx src/codegen/generate-cli.ts",
|
|
54
|
+
"bundle": "tsx src/package-cli.ts",
|
|
55
|
+
"validate": "tsx src/validate-cli.ts",
|
|
56
|
+
"docs": "tsx src/codegen/docs-cli.ts",
|
|
57
|
+
"docs:dev": "cd docs && npm run dev",
|
|
58
|
+
"docs:build": "cd docs && npm install && npm run build",
|
|
59
|
+
"prepack": "npm run generate && npm run bundle && npm run validate && npm run build",
|
|
60
|
+
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && find dist -type f \\( -name \"*.js\" -o -name \"*.js.map\" \\) -delete"
|
|
61
|
+
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"@cedar-policy/cedar-wasm": "4.12.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@intentius/chant": "*",
|
|
67
|
+
"tsc-alias": "^1.8.17",
|
|
68
|
+
"typescript": "^5.9.3"
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"zod": "^4.3.6",
|
|
72
|
+
"@intentius/chant": "^0.44.8",
|
|
73
|
+
"typescript": "^5.9.3"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Ownership channel: Amazon Verified Permissions
|
|
2
|
+
|
|
3
|
+
The design record for chant #1652, epic #1645. The epic left this open ("store-scoped
|
|
4
|
+
until proven finer") because the tier-2 check in `chant dev check-lexicon` exists
|
|
5
|
+
precisely to stop a lexicon declaring a channel it cannot read.
|
|
6
|
+
|
|
7
|
+
The full cedar docs site is #1654's. This file is the decision, kept beside the code
|
|
8
|
+
that implements it so the two cannot drift.
|
|
9
|
+
|
|
10
|
+
## The constraint
|
|
11
|
+
|
|
12
|
+
AVP is the only target chant deploys to where the resource that needs an ownership
|
|
13
|
+
marker cannot carry one.
|
|
14
|
+
|
|
15
|
+
- A **policy store** is taggable. `TagResource` / `ListTagsForResource` take the same
|
|
16
|
+
key/value map every other AWS resource has.
|
|
17
|
+
- An **individual policy** is not. `CreatePolicy` accepts a policy store id, a
|
|
18
|
+
definition, and a client token. There is no tag surface on it, and no
|
|
19
|
+
`TagResource` call that accepts a policy ARN, because a policy has no ARN.
|
|
20
|
+
|
|
21
|
+
That matters because a store is not the unit of ownership. One store holds the
|
|
22
|
+
policies from a chant build *and* whatever anyone added in the console, and the whole
|
|
23
|
+
reason ownership exists is to let `delete` be precise: an undeclared policy carrying
|
|
24
|
+
chant's marker is a safe delete candidate, an undeclared policy without one is
|
|
25
|
+
somebody else's grant and must never be touched. A store-level answer cannot make that
|
|
26
|
+
distinction for anything inside the store.
|
|
27
|
+
|
|
28
|
+
## The decision
|
|
29
|
+
|
|
30
|
+
**Two channels, one set of keys, and only the per-policy one resolves a verdict about
|
|
31
|
+
a policy.**
|
|
32
|
+
|
|
33
|
+
| Channel | Target | Read via | What it answers |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| Description marker | one policy | `ListPolicies` / `GetPolicy` → `definition.static.description` | is *this policy* chant's |
|
|
36
|
+
| Tags | the store | `ListTagsForResource` on the store ARN | is *this store* chant's |
|
|
37
|
+
|
|
38
|
+
Both use `AVP_OWNERSHIP_KEYS` — `chant:managed-by`, `chant:stack`, `chant:env` — the
|
|
39
|
+
same names the aws lexicon stamps into tags. `ownershipChannel` declares one
|
|
40
|
+
`ChannelKeys`, and a lexicon whose two channels disagreed on key names would be two
|
|
41
|
+
conventions wearing one declaration.
|
|
42
|
+
|
|
43
|
+
### The encoding
|
|
44
|
+
|
|
45
|
+
`Definition.Static.Description` is a free-form string, so the marker rides in a
|
|
46
|
+
trailing bracketed segment and the author's own text is preserved ahead of it:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Owners always read their own documents. [chant:managed-by=chant chant:stack=authz chant:env=prod chant:policy-id=owner-read]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- Values are **percent-encoded**, so a stack named `my stack` or an id containing `]`
|
|
53
|
+
round-trips instead of corrupting the segment.
|
|
54
|
+
- `chant:policy-id` is recorded beside the marker but is deliberately **not** part of
|
|
55
|
+
`AVP_OWNERSHIP_KEYS`. It is not an ownership claim — `hasOwnershipMarker` must not
|
|
56
|
+
consult it — it is there because `ListPolicies` returns descriptions and not
|
|
57
|
+
statements, so without it every live policy costs a second `GetPolicy` round trip
|
|
58
|
+
just to learn which chant entity it belongs to.
|
|
59
|
+
- AVP caps the description at **150 characters**. When the author's text plus the
|
|
60
|
+
marker exceed it, the *text* is truncated and the marker is kept whole. The failure
|
|
61
|
+
direction matters: a dropped marker turns an owned policy into a foreign one, a
|
|
62
|
+
foreign policy is never deleted, and the estate would accumulate undeletable
|
|
63
|
+
policies with nothing reported anywhere.
|
|
64
|
+
|
|
65
|
+
### Declared read paths
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
ownershipChannel: {
|
|
69
|
+
keys: AVP_OWNERSHIP_KEYS,
|
|
70
|
+
reads: ["describeResources", "exportResources"],
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Both are implemented and both genuinely read the description:
|
|
75
|
+
|
|
76
|
+
- `describeResources` gets it from the `ListPolicies` enumeration it already runs, so
|
|
77
|
+
the verdict costs nothing extra. Every returned resource carries `owned` or
|
|
78
|
+
`foreign`; with `owned: true`, a policy without the marker becomes NOT-OBSERVED with
|
|
79
|
+
reason `filtered` rather than disappearing (it exists — it just isn't chant's, and a
|
|
80
|
+
declared resource that exists must not classify as `create`).
|
|
81
|
+
- `exportResources` reads the same field to implement its `owned` filter.
|
|
82
|
+
|
|
83
|
+
`observeResourcesDeep` is **not** declared, because this lexicon does not implement it.
|
|
84
|
+
That is the whole point of the per-path declaration (chant #1348): a caller learns
|
|
85
|
+
before asking whether `owned: true` is answerable on the path it is about to use.
|
|
86
|
+
|
|
87
|
+
### Where the verdict is `unknown`
|
|
88
|
+
|
|
89
|
+
Ownership verdicts are total, so every path that cannot read the marker says `unknown`
|
|
90
|
+
rather than degrading silently. In practice this reader never returns a resource whose
|
|
91
|
+
description it did not read — if the enumeration failed, the entity is NOT-OBSERVED,
|
|
92
|
+
not present-with-an-unknown-verdict — so `unknown` shows up only where core stamps it
|
|
93
|
+
for a lexicon that reported nothing. The obligation is still real for anyone extending
|
|
94
|
+
this file: a new read path that cannot see the description must not be added to
|
|
95
|
+
`reads`.
|
|
96
|
+
|
|
97
|
+
## Alternatives rejected
|
|
98
|
+
|
|
99
|
+
**Store-level tags as the only channel.** The epic's provisional position. Rejected
|
|
100
|
+
because it answers a question nobody asks: `lifecycle plan` needs to know whether
|
|
101
|
+
*this policy* is deletable, and "the store is chant's" says nothing about the console
|
|
102
|
+
edit sitting inside it. Kept as the coarse channel (`ownershipFromStoreTags`) because
|
|
103
|
+
it is real information about the store, and free.
|
|
104
|
+
|
|
105
|
+
**A Cedar annotation, `@chant_managed("chant")`, inside the policy text.** Attractive:
|
|
106
|
+
it is in the artifact, it survives any transport, and it needs no second field. Rejected
|
|
107
|
+
because it changes the walk-away artifact. The epic's test is that an emitted policy
|
|
108
|
+
set is consumed by any Cedar evaluator with chant nowhere in sight, and an annotation
|
|
109
|
+
chant invented is chant in sight — it would show up in every export, every `.cedar`
|
|
110
|
+
file, and every console view of the policy. The description is metadata *about* the
|
|
111
|
+
policy in AVP's own model; the statement is the product.
|
|
112
|
+
|
|
113
|
+
**A separate policy-store-scoped index (a marker policy, or an S3 object).** That is a
|
|
114
|
+
state file with extra steps, and the lifecycle model exists specifically so ownership
|
|
115
|
+
lives on the resource rather than in something chant has to host and lock.
|
|
116
|
+
|
|
117
|
+
## Cost, stated plainly
|
|
118
|
+
|
|
119
|
+
The description is user-writable. Someone can edit it in the console and a chant-owned
|
|
120
|
+
policy becomes `foreign`, which means chant stops offering to delete it — noticeable,
|
|
121
|
+
and safe. The reverse is also possible: pasting chant's marker onto a foreign policy
|
|
122
|
+
makes chant willing to delete it. Tags have exactly the same property, so this is the
|
|
123
|
+
ordinary AWS trust model rather than a new weakness, but the description is more likely
|
|
124
|
+
to be edited by hand than a tag is. Worth knowing before turning on `--owned`
|
|
125
|
+
reconciliation against a shared store.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { ResourceMetadata } from "@intentius/chant/lexicon";
|
|
3
|
+
import { CEDAR_POLICY_TYPE } from "../serializer";
|
|
4
|
+
import { AVP_AMBIENT_KINDS, observeAvpAmbient } from "./ambient";
|
|
5
|
+
import {
|
|
6
|
+
markedDescription,
|
|
7
|
+
mockAvpTransport,
|
|
8
|
+
statementFor,
|
|
9
|
+
MOCK_ENV,
|
|
10
|
+
type MockStore,
|
|
11
|
+
} from "./testdata/mock-transport";
|
|
12
|
+
|
|
13
|
+
const STORE = "PS-abc123";
|
|
14
|
+
|
|
15
|
+
/** One declared-and-observed policy, plus two nobody declared. */
|
|
16
|
+
const store: MockStore = {
|
|
17
|
+
policyStoreId: STORE,
|
|
18
|
+
policies: [
|
|
19
|
+
{ policyId: "p-1", statement: statementFor("owner-read"), description: markedDescription("owner-read") },
|
|
20
|
+
{
|
|
21
|
+
policyId: "p-99",
|
|
22
|
+
statement: '@id("console-grant")\npermit (\n principal,\n action,\n resource\n);',
|
|
23
|
+
description: "temporary, added during the incident",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
policyId: "p-100",
|
|
27
|
+
statement: 'forbid (\n principal,\n action,\n resource is App::Document\n)\nwhen { resource.classification == "secret" };',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const observed: Record<string, ResourceMetadata> = {
|
|
33
|
+
ownerRead: { type: CEDAR_POLICY_TYPE, status: "STATIC", physicalId: "p-1", ownership: "owned" },
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function run(options: Partial<Parameters<typeof observeAvpAmbient>[0]> = {}) {
|
|
37
|
+
const { http } = mockAvpTransport(store);
|
|
38
|
+
return observeAvpAmbient({
|
|
39
|
+
environment: "prod",
|
|
40
|
+
kinds: [CEDAR_POLICY_TYPE],
|
|
41
|
+
observed,
|
|
42
|
+
policyStoreId: STORE,
|
|
43
|
+
client: { http },
|
|
44
|
+
env: MOCK_ENV,
|
|
45
|
+
...options,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
describe("ambientKinds", () => {
|
|
50
|
+
it("names the one kind cedar deploys", () => {
|
|
51
|
+
expect([...AVP_AMBIENT_KINDS]).toEqual([CEDAR_POLICY_TYPE]);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("observeAvpAmbient", () => {
|
|
56
|
+
it("finds the policies nothing declares and excludes the one that is managed", async () => {
|
|
57
|
+
const ambient = await run();
|
|
58
|
+
expect(Object.keys(ambient).sort()).toEqual(["policy/p-100", "policy/p-99"]);
|
|
59
|
+
expect(ambient["policy/p-99"].ambient).toBe(true);
|
|
60
|
+
expect(ambient["policy/p-99"].type).toBe(CEDAR_POLICY_TYPE);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("carries the statement and its effect, and stops short of a verdict", async () => {
|
|
64
|
+
const ambient = await run();
|
|
65
|
+
|
|
66
|
+
// The standing grant: a bare permit nobody declared.
|
|
67
|
+
expect(ambient["policy/p-99"].attributes?.effect).toBe("permit");
|
|
68
|
+
expect(ambient["policy/p-99"].attributes?.statement).toContain("permit");
|
|
69
|
+
expect(ambient["policy/p-100"].attributes?.effect).toBe("forbid");
|
|
70
|
+
|
|
71
|
+
// Nothing in the metadata calls it dangerous — that is the consumer's call.
|
|
72
|
+
expect(Object.keys(ambient["policy/p-99"])).not.toContain("standingGrant");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("reports the ownership verdict it can read from the description", async () => {
|
|
76
|
+
const ambient = await run();
|
|
77
|
+
expect(ambient["policy/p-99"].ownership).toBe("foreign");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("does not call a declared-but-unobserved policy ambient", async () => {
|
|
81
|
+
// ownerRead's read failed, so it has no physicalId in `observed` — the
|
|
82
|
+
// declared-id exclusion is what stops a hole becoming a security finding.
|
|
83
|
+
const ambient = await run({ observed: {}, declaredPolicyIds: ["owner-read"] });
|
|
84
|
+
expect(Object.keys(ambient).sort()).toEqual(["policy/p-100", "policy/p-99"]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("returns nothing when the project declares no policies", async () => {
|
|
88
|
+
expect(await run({ kinds: ["App::User"] })).toEqual({});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("degrades to empty rather than sinking a managed observation that succeeded", async () => {
|
|
92
|
+
const broken = await run({
|
|
93
|
+
client: { http: mockAvpTransport({ ...store, listFails: { code: "ThrottlingException", message: "Rate exceeded", status: 429 } }).http },
|
|
94
|
+
});
|
|
95
|
+
expect(broken).toEqual({});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("returns nothing when nothing binds the environment to a store", async () => {
|
|
99
|
+
const { http } = mockAvpTransport(store);
|
|
100
|
+
const ambient = await observeAvpAmbient({
|
|
101
|
+
environment: "prod",
|
|
102
|
+
kinds: [CEDAR_POLICY_TYPE],
|
|
103
|
+
observed,
|
|
104
|
+
client: { http },
|
|
105
|
+
env: MOCK_ENV,
|
|
106
|
+
});
|
|
107
|
+
expect(ambient).toEqual({});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("returns nothing when there are no credentials", async () => {
|
|
111
|
+
expect(await run({ env: {} })).toEqual({});
|
|
112
|
+
});
|
|
113
|
+
});
|