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