@zapier/policy-schema 0.13.0 → 0.17.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/index.mjs CHANGED
@@ -1,9 +1,15 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  // src/schema.ts
4
- var VALID_VERSIONS = [1, 2];
4
+ var RUBBERDUCK_POLICY_VERSION = "rc-rubberduck";
5
+ var VALID_VERSIONS = [1, 2, 3, RUBBERDUCK_POLICY_VERSION];
6
+ var ID_BASED_VERSIONS = /* @__PURE__ */ new Set([
7
+ 3,
8
+ RUBBERDUCK_POLICY_VERSION
9
+ ]);
5
10
  var VALID_EFFECTS = ["allow", "ask", "deny"];
6
11
  var VALID_OPERATOR_LIST = [
12
+ "all",
7
13
  "equals",
8
14
  "exists",
9
15
  "in",
@@ -12,16 +18,20 @@ var VALID_OPERATOR_LIST = [
12
18
  "matches_host",
13
19
  "matches_path",
14
20
  "matches_url",
21
+ "none",
15
22
  "range",
16
23
  "size",
24
+ "some",
17
25
  "values_in"
18
26
  ];
19
27
  var ScalarSchema = z.union([z.string(), z.number(), z.boolean()]);
20
28
  var PathSchema = z.array(z.union([z.string(), z.number()])).min(1);
29
+ var RelativePathSchema = z.array(z.union([z.string(), z.number()]));
30
+ var ConditionValueSchema = z.json();
21
31
  var EqualsConditionSchema = z.object({
22
32
  path: PathSchema,
23
33
  operator: z.literal("equals"),
24
- value: z.json()
34
+ value: ConditionValueSchema
25
35
  }).strict();
26
36
  var InConditionSchema = z.object({
27
37
  path: PathSchema,
@@ -82,6 +92,24 @@ var SizeConditionSchema = z.object({
82
92
  operator: z.literal("size"),
83
93
  value: SizeValueSchema
84
94
  }).strict();
95
+ var RelativeConditionSchema = z.union([
96
+ EqualsConditionSchema.extend({ path: RelativePathSchema }),
97
+ InConditionSchema.extend({ path: RelativePathSchema }),
98
+ MatchesConditionSchema.extend({ path: RelativePathSchema }),
99
+ MatchesHostConditionSchema.extend({ path: RelativePathSchema }),
100
+ MatchesPathConditionSchema.extend({ path: RelativePathSchema }),
101
+ RangeConditionSchema.extend({ path: RelativePathSchema }),
102
+ MatchesUrlConditionSchema.extend({ path: RelativePathSchema }),
103
+ ValuesInConditionSchema.extend({ path: RelativePathSchema }),
104
+ ExistsConditionSchema.extend({ path: RelativePathSchema }),
105
+ KeysInConditionSchema.extend({ path: RelativePathSchema }),
106
+ SizeConditionSchema.extend({ path: RelativePathSchema })
107
+ ]);
108
+ var QuantifierConditionSchema = z.object({
109
+ path: PathSchema,
110
+ operator: z.enum(["some", "all", "none"]),
111
+ where: z.array(RelativeConditionSchema).min(1)
112
+ }).strict();
85
113
  var ConditionSchema = z.union([
86
114
  // Scalar operators
87
115
  EqualsConditionSchema,
@@ -97,7 +125,8 @@ var ConditionSchema = z.union([
97
125
  // Other operators
98
126
  ExistsConditionSchema,
99
127
  KeysInConditionSchema,
100
- SizeConditionSchema
128
+ SizeConditionSchema,
129
+ QuantifierConditionSchema
101
130
  ]);
102
131
  var ScalarOperatorSchema = z.enum([
103
132
  "equals",
@@ -107,7 +136,7 @@ var ScalarOperatorSchema = z.enum([
107
136
  "matches_path",
108
137
  "range"
109
138
  ]);
110
- var ArrayOperatorSchema = z.enum(["values_in"]);
139
+ var ArrayOperatorSchema = z.enum(["values_in", "some", "all", "none"]);
111
140
  var UrlOperatorSchema = z.enum(["matches_url"]);
112
141
  var OtherOperatorSchema = z.enum(["exists", "keys_in", "size"]);
113
142
  var OperatorSchema = z.enum([
@@ -122,12 +151,14 @@ var OperatorSchema = z.enum([
122
151
  "matches_url",
123
152
  // Array
124
153
  "values_in",
154
+ "some",
155
+ "all",
156
+ "none",
125
157
  // Other
126
158
  "exists",
127
159
  "keys_in",
128
160
  "size"
129
161
  ]);
130
- var ConditionValueSchema = z.json();
131
162
  var V1StatementSchema = z.object({
132
163
  id: z.string().optional(),
133
164
  effect: z.enum(["allow", "deny"]),
@@ -143,92 +174,182 @@ var V2StatementSchema = z.object({
143
174
  conditions: z.array(ConditionSchema).optional()
144
175
  }).strict();
145
176
  var StatementSchema = V2StatementSchema;
177
+ var StatementIdSchema = z.string().regex(/^s_\S+$/, { error: "Statement id must match ^s_\\S+$" }).max(512);
178
+ var PolicyIdSchema = z.string().regex(/^p_\S+$/, { error: "Policy id must match ^p_\\S+$" }).max(512);
146
179
  var V1PolicySchema = z.object({
147
180
  version: z.literal(1),
148
181
  statements: z.array(V1StatementSchema)
149
182
  }).strict();
183
+ var DefaultEffectSchema = z.enum(["deny", "ask"]);
150
184
  var V2PolicySchema = z.object({
151
185
  version: z.literal(2),
152
186
  statements: z.array(V2StatementSchema)
153
187
  }).strict();
154
- var PolicySchema = z.union([V1PolicySchema, V2PolicySchema]);
188
+ var RcRubberduckStatementSchema = z.object({
189
+ id: StatementIdSchema,
190
+ effect: z.enum(["allow", "ask", "deny"]),
191
+ permissions: z.array(z.string()).min(1),
192
+ resources: z.array(z.string()).min(1),
193
+ conditions: z.array(ConditionSchema).optional()
194
+ }).strict();
195
+ var RcRubberduckPolicySchema = z.object({
196
+ version: z.literal(RUBBERDUCK_POLICY_VERSION),
197
+ id: PolicyIdSchema,
198
+ defaultEffect: DefaultEffectSchema,
199
+ statements: z.array(RcRubberduckStatementSchema)
200
+ }).strict();
201
+ var V3_MAX_TIERS = 3;
202
+ var V3StatementSchema = z.object({
203
+ id: StatementIdSchema,
204
+ effect: z.enum(["allow", "ask", "deny"]),
205
+ permissions: z.array(z.string()).min(1),
206
+ resources: z.array(z.string()).min(1),
207
+ conditions: z.array(ConditionSchema).optional()
208
+ }).strict();
209
+ var V3PolicySchema = z.object({
210
+ version: z.literal(3),
211
+ // Ordered tiers, in priority order. 1..V3_MAX_TIERS tiers; a tier may be
212
+ // empty (e.g. `[[]]` denies everything).
213
+ statements: z.array(z.array(V3StatementSchema)).min(1).max(V3_MAX_TIERS)
214
+ }).strict();
215
+ var PolicySchema = z.union([
216
+ V1PolicySchema,
217
+ V2PolicySchema,
218
+ V3PolicySchema,
219
+ RcRubberduckPolicySchema
220
+ ]);
155
221
  var CanonicalHashSchema = z.custom(
156
222
  (value) => typeof value === "string" && /^[0-9a-f]{64}$/.test(value),
157
223
  { error: "Expected a 64-character lowercase hex SHA-256 digest" }
158
224
  );
159
- var OverrideBaseShape = {
225
+ var OverrideHashBaseShape = {
160
226
  id: z.string().optional(),
227
+ /**
228
+ * The canonical hash of the targeted statement.
229
+ * Used for v1/v2 policies. Must be a valid SHA-256 hex digest (non-null).
230
+ */
161
231
  statementHash: CanonicalHashSchema,
162
232
  effect: z.literal("allow")
163
233
  };
234
+ var OverrideIdBaseShape = {
235
+ id: z.string().optional(),
236
+ /**
237
+ * The statement id of the targeted statement. Used for rc-rubberduck policies.
238
+ * Must be a valid statement id (non-null). To target the policy's default
239
+ * effect, use policyId instead.
240
+ */
241
+ statementId: StatementIdSchema,
242
+ effect: z.literal("allow")
243
+ };
244
+ var OverridePolicyIdBaseShape = {
245
+ id: z.string().optional(),
246
+ /**
247
+ * Targets the policy's default effect. Only applies when this policyId
248
+ * matches the evaluated policy's id. Used for rc-rubberduck policies.
249
+ */
250
+ policyId: PolicyIdSchema,
251
+ effect: z.literal("allow")
252
+ };
164
253
  var OverrideSchema = z.union([
165
- z.object({ ...OverrideBaseShape, match: z.literal("any") }).strict(),
166
- z.object({ ...OverrideBaseShape, contextHash: CanonicalHashSchema }).strict(),
167
- // Empty conditionals is intentional (the "blank check"): the override
168
- // applies whenever the targeted ask statement fires, with no extra
169
- // conditions. Do not add .min(1) here.
254
+ // statementHash × match
255
+ z.object({ ...OverrideHashBaseShape, match: z.literal("any") }).strict(),
256
+ // statementHash × contextHash
257
+ z.object({ ...OverrideHashBaseShape, contextHash: CanonicalHashSchema }).strict(),
258
+ // statementHash × conditionals
259
+ z.object({
260
+ ...OverrideHashBaseShape,
261
+ conditionals: z.array(ConditionSchema)
262
+ }).strict(),
263
+ // statementId × match
264
+ z.object({ ...OverrideIdBaseShape, match: z.literal("any") }).strict(),
265
+ // statementId × contextHash
266
+ z.object({ ...OverrideIdBaseShape, contextHash: CanonicalHashSchema }).strict(),
267
+ // statementId × conditionals
268
+ z.object({
269
+ ...OverrideIdBaseShape,
270
+ conditionals: z.array(ConditionSchema)
271
+ }).strict(),
272
+ // policyId × match
273
+ z.object({ ...OverridePolicyIdBaseShape, match: z.literal("any") }).strict(),
274
+ // policyId × contextHash
275
+ z.object({ ...OverridePolicyIdBaseShape, contextHash: CanonicalHashSchema }).strict(),
276
+ // policyId × conditionals
170
277
  z.object({
171
- ...OverrideBaseShape,
278
+ ...OverridePolicyIdBaseShape,
172
279
  conditionals: z.array(ConditionSchema)
173
280
  }).strict()
174
281
  ]);
175
282
  function getOpenApiSchema() {
176
- return {
283
+ const statementItems = {
177
284
  type: "object",
178
- required: ["version", "statements"],
285
+ required: ["effect", "permissions", "resources"],
179
286
  properties: {
180
- version: {
181
- type: "integer",
182
- enum: [...VALID_VERSIONS],
183
- description: `Policy schema version. Must be one of ${JSON.stringify([...VALID_VERSIONS])}.`
287
+ id: {
288
+ type: "string",
289
+ description: "Statement identifier. Required for v3 and rc-rubberduck policies; must use the s_ prefix with no whitespace. Optional on v1/v2 for debugging and provenance."
184
290
  },
185
- statements: {
291
+ effect: {
292
+ type: "string",
293
+ enum: [...VALID_EFFECTS].sort(),
294
+ description: "Whether to allow, ask, or deny the specified permissions. The 'ask' effect is valid in version 2, version 3, and rc-rubberduck policies."
295
+ },
296
+ permissions: {
297
+ type: "array",
298
+ minItems: 1,
299
+ items: { type: "string" },
300
+ description: "List of permission identifiers."
301
+ },
302
+ resources: {
186
303
  type: "array",
187
- description: "List of policy statements defining permissions.",
304
+ minItems: 1,
305
+ items: { type: "string" },
306
+ description: "List of resource identifiers the statement applies to."
307
+ },
308
+ conditions: {
309
+ type: "array",
310
+ description: "Optional conditions that must be met for the statement to apply.",
188
311
  items: {
189
312
  type: "object",
190
- required: ["effect", "permissions", "resources"],
313
+ required: ["path", "operator"],
191
314
  properties: {
192
- id: {
193
- type: "string",
194
- description: "Optional identifier for debugging and provenance. Included in canonical hash."
315
+ path: {
316
+ type: "array",
317
+ minItems: 1,
318
+ description: "Path to the value to evaluate in the request context.",
319
+ items: {
320
+ anyOf: [{ type: "string" }, { type: "integer" }]
321
+ }
195
322
  },
196
- effect: {
323
+ operator: {
197
324
  type: "string",
198
- enum: [...VALID_EFFECTS].sort(),
199
- description: "Whether to allow, ask, or deny the specified permissions. The 'ask' effect is only valid in version 2 policies."
325
+ enum: [...VALID_OPERATOR_LIST],
326
+ description: "Comparison operator used to evaluate the condition."
200
327
  },
201
- permissions: {
202
- type: "array",
203
- minItems: 1,
204
- items: { type: "string" },
205
- description: "List of permission identifiers."
328
+ value: {
329
+ description: "Value to compare against using the operator."
206
330
  },
207
- resources: {
331
+ where: {
208
332
  type: "array",
209
333
  minItems: 1,
210
- items: { type: "string" },
211
- description: "List of resource identifiers the statement applies to."
212
- },
213
- conditions: {
214
- type: "array",
215
- description: "Optional conditions that must be met for the statement to apply.",
334
+ description: "Conditions evaluated relative to each collection element for some, all, and none.",
216
335
  items: {
217
336
  type: "object",
218
337
  required: ["path", "operator"],
219
338
  properties: {
220
339
  path: {
221
340
  type: "array",
222
- minItems: 1,
223
- description: "Path to the value to evaluate in the request context.",
341
+ minItems: 0,
342
+ description: "Path relative to the collection element; an empty path selects the element itself.",
224
343
  items: {
225
344
  anyOf: [{ type: "string" }, { type: "integer" }]
226
345
  }
227
346
  },
228
347
  operator: {
229
348
  type: "string",
230
- enum: [...VALID_OPERATOR_LIST],
231
- description: "Comparison operator used to evaluate the condition."
349
+ enum: [...VALID_OPERATOR_LIST].filter(
350
+ (operator) => operator !== "some" && operator !== "all" && operator !== "none"
351
+ ),
352
+ description: "Non-quantifier operator evaluated against the collection element."
232
353
  },
233
354
  value: {
234
355
  description: "Value to compare against using the operator."
@@ -241,6 +362,42 @@ function getOpenApiSchema() {
241
362
  }
242
363
  }
243
364
  };
365
+ return {
366
+ type: "object",
367
+ required: ["version", "statements"],
368
+ properties: {
369
+ version: {
370
+ anyOf: [
371
+ { type: "integer", enum: [1, 2, 3] },
372
+ { type: "string", enum: [RUBBERDUCK_POLICY_VERSION] }
373
+ ],
374
+ description: "Policy schema version. Stable versions are 1, 2, and 3; supported release candidates are rc-rubberduck."
375
+ },
376
+ id: {
377
+ type: "string",
378
+ description: "Policy id (required for rc-rubberduck policies; not permitted on any other version). Must use the p_ prefix and contain no whitespace."
379
+ },
380
+ defaultEffect: {
381
+ type: "string",
382
+ enum: [...DefaultEffectSchema.options],
383
+ description: "Effect applied when no statement matches. Required on rc-rubberduck policies (deny equals implicit deny); not permitted on any other version."
384
+ },
385
+ statements: {
386
+ type: "array",
387
+ description: "Policy statements. For v1/v2/rc-rubberduck each entry is a statement. For v3 each entry is a tier (a list of statements); tiers are in priority order and evaluated first-to-last (the first tier with a match decides), so `statements` is a list of lists.",
388
+ items: {
389
+ anyOf: [
390
+ statementItems,
391
+ {
392
+ type: "array",
393
+ description: "A v3 tier: statements evaluated together with deny > ask > allow precedence.",
394
+ items: statementItems
395
+ }
396
+ ]
397
+ }
398
+ }
399
+ }
400
+ };
244
401
  }
245
402
 
246
- export { ArrayOperatorSchema, CanonicalHashSchema, ConditionSchema, ConditionValueSchema, OperatorSchema, OtherOperatorSchema, OverrideSchema, PolicySchema, RangeValueSchema, ScalarOperatorSchema, SizeValueSchema, StatementSchema, UrlOperatorSchema, VALID_EFFECTS, VALID_OPERATOR_LIST, VALID_VERSIONS, getOpenApiSchema };
403
+ export { ArrayOperatorSchema, CanonicalHashSchema, ConditionSchema, ConditionValueSchema, DefaultEffectSchema, ID_BASED_VERSIONS, OperatorSchema, OtherOperatorSchema, OverrideSchema, PolicyIdSchema, PolicySchema, RUBBERDUCK_POLICY_VERSION, RangeValueSchema, ScalarOperatorSchema, SizeValueSchema, StatementIdSchema, StatementSchema, UrlOperatorSchema, V3_MAX_TIERS, VALID_EFFECTS, VALID_OPERATOR_LIST, VALID_VERSIONS, getOpenApiSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/policy-schema",
3
- "version": "0.13.0",
3
+ "version": "0.17.0",
4
4
  "description": "Zod schemas and TypeScript types for the Zapier policy engine",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",