@zapier/policy-context 2.0.0 → 3.0.0-mr.39.bc800a62
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 +45 -2
- package/dist/index.cjs +241 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +732 -6
- package/dist/index.d.ts +732 -6
- package/dist/index.js +239 -35
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ npm install @zapier/policy-context zod
|
|
|
16
16
|
|
|
17
17
|
## What it provides
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Four layers, each building on the previous:
|
|
20
20
|
|
|
21
21
|
1. **Types** — Zod schemas for `HttpRequestContext`, `ActionRunContext`, and the
|
|
22
22
|
`RequestContext` discriminated union. The canonical shapes stored in
|
|
@@ -25,7 +25,11 @@ Three layers, each building on the previous:
|
|
|
25
25
|
body coercion) into validated contexts.
|
|
26
26
|
3. **Conditions / Policies** — Convert stored contexts into
|
|
27
27
|
[`@zapier/policy-schema`](https://www.npmjs.com/package/@zapier/policy-schema)
|
|
28
|
-
`Condition[]` and full
|
|
28
|
+
`Condition[]` and full policy documents. Canonical builders emit v4 with one
|
|
29
|
+
rule tier (`rules: [[...]]`) and randomly generated `r_<uuid>` rule IDs;
|
|
30
|
+
`toExactPolicyV3` is an explicit legacy exact-request escape hatch.
|
|
31
|
+
4. **Zapier policy validation** — `ZapierPolicySchema` composes the generic
|
|
32
|
+
`PolicySchema` with Zapier resource grammar and permission/resource rules.
|
|
29
33
|
|
|
30
34
|
## Usage
|
|
31
35
|
|
|
@@ -65,6 +69,27 @@ import { toExactPolicy } from "@zapier/policy-context";
|
|
|
65
69
|
const policy = toExactPolicy(context);
|
|
66
70
|
```
|
|
67
71
|
|
|
72
|
+
`toExactPolicy` is the canonical exact builder and returns
|
|
73
|
+
`Extract<Policy, { version: 4 }>`, with a single rule tier (`rules: [[...]]`)
|
|
74
|
+
and `r_<uuid>` rule IDs. Rule content and ordering are unchanged. Migrate
|
|
75
|
+
`policy.statements[0][i]` to `policy.rules[0][i]` and any statement-ID prefix
|
|
76
|
+
handling from `s_` to `r_`.
|
|
77
|
+
|
|
78
|
+
For a legacy consumer that still requires an exact v3 document, use the explicit
|
|
79
|
+
factory instead:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { toExactPolicyV3 } from "@zapier/policy-context";
|
|
83
|
+
|
|
84
|
+
const legacyPolicy = toExactPolicyV3(context);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`toExactPolicyV3` returns `Extract<Policy, { version: 3 }>`, with `version: 3`,
|
|
88
|
+
one statement tier (`statements: [[...]]`), and `s_<uuid>` statement IDs. It
|
|
89
|
+
preserves the canonical exact builder's conditions, content, and ordering.
|
|
90
|
+
`buildConnectionPolicy` and `buildConnectionlessPolicy` remain v4; this escape
|
|
91
|
+
hatch is only for exact-request policies.
|
|
92
|
+
|
|
68
93
|
### Connection-scoped policies
|
|
69
94
|
|
|
70
95
|
```ts
|
|
@@ -76,6 +101,24 @@ const policy = buildConnectionPolicy({
|
|
|
76
101
|
});
|
|
77
102
|
```
|
|
78
103
|
|
|
104
|
+
### Validate a Zapier policy
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { ZapierPolicySchema } from "@zapier/policy-context";
|
|
108
|
+
|
|
109
|
+
const policy = ZapierPolicySchema.parse(input);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`ZapierPolicySchema` output includes v4 documents as well as earlier versions.
|
|
113
|
+
This accepts `http`, `connection/{id|*}`, canonical
|
|
114
|
+
`action/{selected_api}/{action_type|*}/{action_key}` patterns, and the lone `*`
|
|
115
|
+
god-mode resource. It validates structure only; app, API version, and action-key
|
|
116
|
+
existence remain the caller's responsibility. It also rejects a statement or rule
|
|
117
|
+
when one higher-precedence statement or rule (`deny` > `ask` > `allow`) fully
|
|
118
|
+
subsumes it. Validation traverses each ordered tier of v3 `statements` and v4
|
|
119
|
+
`rules`, checking masking only within that tier. Same-effect redundancy and
|
|
120
|
+
coverage assembled from multiple statements or rules remain valid.
|
|
121
|
+
|
|
79
122
|
## License
|
|
80
123
|
|
|
81
124
|
Published as part of Zapier's services. Use is governed by the
|
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AccessLevelSchema: () => AccessLevelSchema,
|
|
24
|
+
ActionResourcePatternSchema: () => ActionResourcePatternSchema,
|
|
24
25
|
ActionRunContextInputSchema: () => ActionRunContextInputSchema,
|
|
25
26
|
ActionRunContextSchema: () => ActionRunContextSchema,
|
|
26
27
|
ActionTypeSchema: () => ActionTypeSchema,
|
|
@@ -33,6 +34,7 @@ __export(index_exports, {
|
|
|
33
34
|
READ_ACTION_TYPES: () => READ_ACTION_TYPES,
|
|
34
35
|
READ_HTTP_METHODS: () => READ_HTTP_METHODS,
|
|
35
36
|
RequestContextSchema: () => RequestContextSchema,
|
|
37
|
+
ZapierPolicySchema: () => ZapierPolicySchema,
|
|
36
38
|
buildActionResource: () => buildActionResource,
|
|
37
39
|
buildActionRunContext: () => buildActionRunContext,
|
|
38
40
|
buildBroadActionResource: () => buildBroadActionResource,
|
|
@@ -49,12 +51,28 @@ __export(index_exports, {
|
|
|
49
51
|
parseActionResource: () => parseActionResource,
|
|
50
52
|
parseUrl: () => parseUrl,
|
|
51
53
|
toAppKey: () => toAppKey,
|
|
52
|
-
toExactPolicy: () => toExactPolicy
|
|
54
|
+
toExactPolicy: () => toExactPolicy,
|
|
55
|
+
toExactPolicyV3: () => toExactPolicyV3
|
|
53
56
|
});
|
|
54
57
|
module.exports = __toCommonJS(index_exports);
|
|
55
58
|
|
|
56
59
|
// src/types.ts
|
|
57
60
|
var import_zod = require("zod");
|
|
61
|
+
function containsUnsafeInteger(value) {
|
|
62
|
+
if (typeof value === "number") {
|
|
63
|
+
return Number.isInteger(value) && !Number.isSafeInteger(value);
|
|
64
|
+
}
|
|
65
|
+
if (Array.isArray(value)) {
|
|
66
|
+
return value.some(containsUnsafeInteger);
|
|
67
|
+
}
|
|
68
|
+
if (value !== null && typeof value === "object") {
|
|
69
|
+
return Object.values(value).some(containsUnsafeInteger);
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
var SafeJsonValueSchema = import_zod.z.json().refine((value) => !containsUnsafeInteger(value), {
|
|
74
|
+
message: "JSON integers must be within the IEEE-754 safe range; encode larger identifiers as strings"
|
|
75
|
+
});
|
|
58
76
|
var ParsedUrlSchema = import_zod.z.object({
|
|
59
77
|
scheme: import_zod.z.string().describe('URL scheme, e.g. "https"'),
|
|
60
78
|
host: import_zod.z.string().describe('Hostname, e.g. "api.example.com"'),
|
|
@@ -113,7 +131,7 @@ var HttpRequestContextSchema = import_zod.z.object({
|
|
|
113
131
|
* `z.json()` still rejects non-JSON values (`Date`, `undefined`,
|
|
114
132
|
* functions) at parse time. Same inferred `JsonValue` type either way.
|
|
115
133
|
*/
|
|
116
|
-
body_json: import_zod.z.unknown().pipe(
|
|
134
|
+
body_json: import_zod.z.unknown().pipe(SafeJsonValueSchema).nullable().describe("Parsed JSON body (if applicable)"),
|
|
117
135
|
/** Zapier connection ID used for this request */
|
|
118
136
|
connection_id: import_zod.z.string().max(255).optional().describe("Zapier connection ID used for this request"),
|
|
119
137
|
/** Optional human-readable reason for the request */
|
|
@@ -131,6 +149,11 @@ var ActionTypeSchema = import_zod.z.enum([
|
|
|
131
149
|
]).meta({ id: "action_type", description: "Canonical Zapier action type." });
|
|
132
150
|
var ACTION_RESOURCE_SEGMENT_PATTERN = /^[A-Za-z0-9._+-]+(?:@[A-Za-z0-9._+-]+)?$/;
|
|
133
151
|
var ACTION_KEY_SEGMENT_PATTERN = /^[A-Za-z0-9._+:-]+(?:@[A-Za-z0-9._+:-]+)?$/;
|
|
152
|
+
var CONNECTION_ID_CHARS = "[^/*\\x00-\\x20\\x7f-\\xa0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]";
|
|
153
|
+
var CONNECTION_ID_PATTERN = new RegExp(`^${CONNECTION_ID_CHARS}+$`);
|
|
154
|
+
var CONNECTION_RESOURCE_PATTERN = new RegExp(
|
|
155
|
+
`^connection/(?:\\*|${CONNECTION_ID_CHARS}+)$`
|
|
156
|
+
);
|
|
134
157
|
function parseActionResource(resource) {
|
|
135
158
|
const segments = resource.split("/");
|
|
136
159
|
if (segments.length !== 4 || segments[0] !== "action") {
|
|
@@ -191,7 +214,7 @@ var ActionRunContextInputSchema = import_zod.z.object({
|
|
|
191
214
|
}),
|
|
192
215
|
connection_id: import_zod.z.string().max(255).optional().describe("Zapier connection ID used for this request"),
|
|
193
216
|
/** Action input parameters. See body_json above for the pipe rationale. */
|
|
194
|
-
inputs: import_zod.z.unknown().pipe(
|
|
217
|
+
inputs: import_zod.z.unknown().pipe(SafeJsonValueSchema).optional().describe("Action input parameters"),
|
|
195
218
|
reason: import_zod.z.string().max(1024).optional().describe("Optional human-readable reason for the request")
|
|
196
219
|
}).meta({
|
|
197
220
|
id: "ActionRunContext",
|
|
@@ -222,7 +245,7 @@ var ActionRunContextSchema = ActionRunContextInputSchema.transform(
|
|
|
222
245
|
...input.connection_id !== void 0 && {
|
|
223
246
|
connection_id: input.connection_id
|
|
224
247
|
},
|
|
225
|
-
...input.inputs !== void 0 && { inputs: input.inputs },
|
|
248
|
+
...input.inputs !== void 0 && input.inputs !== null && { inputs: input.inputs },
|
|
226
249
|
...input.reason !== void 0 && { reason: input.reason }
|
|
227
250
|
};
|
|
228
251
|
}
|
|
@@ -297,14 +320,14 @@ function buildExactHttpConditions(context) {
|
|
|
297
320
|
conditions.push({
|
|
298
321
|
path: ["url", "query", key],
|
|
299
322
|
operator: "equals",
|
|
300
|
-
value: values
|
|
323
|
+
value: structuredClone(values)
|
|
301
324
|
});
|
|
302
325
|
}
|
|
303
326
|
if (context.body_json !== null) {
|
|
304
327
|
conditions.push({
|
|
305
328
|
path: ["body_json"],
|
|
306
329
|
operator: "equals",
|
|
307
|
-
value: context.body_json
|
|
330
|
+
value: structuredClone(context.body_json)
|
|
308
331
|
});
|
|
309
332
|
} else if (context.body !== null) {
|
|
310
333
|
conditions.push({
|
|
@@ -339,7 +362,7 @@ function buildExactActionConditions(context) {
|
|
|
339
362
|
conditions.push({
|
|
340
363
|
path: ["inputs"],
|
|
341
364
|
operator: "equals",
|
|
342
|
-
value: context.inputs
|
|
365
|
+
value: structuredClone(context.inputs)
|
|
343
366
|
});
|
|
344
367
|
}
|
|
345
368
|
return conditions;
|
|
@@ -364,7 +387,7 @@ var BuildHttpRequestContextOptionsSchema = import_zod2.z.object({
|
|
|
364
387
|
/** Raw body string */
|
|
365
388
|
body: import_zod2.z.string().nullable().optional(),
|
|
366
389
|
/** Pre-parsed JSON body (e.g. from Fastify's automatic JSON parsing) */
|
|
367
|
-
body_json:
|
|
390
|
+
body_json: SafeJsonValueSchema.optional(),
|
|
368
391
|
/** Connection ID associated with this request */
|
|
369
392
|
connection_id: import_zod2.z.string().optional()
|
|
370
393
|
});
|
|
@@ -413,7 +436,7 @@ var BuildActionRunContextOptionsSchema = import_zod2.z.object({
|
|
|
413
436
|
message: "action_key must contain only [A-Za-z0-9._@:+-] (no globs, slashes, or whitespace)"
|
|
414
437
|
}),
|
|
415
438
|
connection_id: import_zod2.z.string().optional(),
|
|
416
|
-
inputs:
|
|
439
|
+
inputs: SafeJsonValueSchema.optional(),
|
|
417
440
|
reason: import_zod2.z.string().optional()
|
|
418
441
|
});
|
|
419
442
|
function buildActionRunContext(options) {
|
|
@@ -432,56 +455,212 @@ function buildActionRunContext(options) {
|
|
|
432
455
|
}
|
|
433
456
|
|
|
434
457
|
// src/policies.ts
|
|
435
|
-
function buildAuthStatement(authenticationId) {
|
|
458
|
+
function buildAuthStatement(authenticationId, idPrefix) {
|
|
436
459
|
return {
|
|
460
|
+
id: `${idPrefix}_${crypto.randomUUID()}`,
|
|
437
461
|
effect: "allow",
|
|
438
462
|
permissions: ["can_use"],
|
|
439
463
|
resources: [`connection/${authenticationId}`],
|
|
440
464
|
conditions: []
|
|
441
465
|
};
|
|
442
466
|
}
|
|
443
|
-
function
|
|
444
|
-
const statements = [];
|
|
445
|
-
if (context.connection_id) {
|
|
446
|
-
statements.push(buildAuthStatement(context.connection_id));
|
|
447
|
-
}
|
|
467
|
+
function httpContextToTier(context, idPrefix) {
|
|
448
468
|
const conditions = buildExactHttpConditions(context);
|
|
449
469
|
conditions.push({
|
|
450
470
|
path: ["url", "query"],
|
|
451
471
|
operator: "keys_in",
|
|
452
472
|
value: Object.keys(context.url.query)
|
|
453
473
|
});
|
|
454
|
-
|
|
474
|
+
const statement = {
|
|
475
|
+
id: `${idPrefix}_${crypto.randomUUID()}`,
|
|
455
476
|
effect: "allow",
|
|
456
477
|
permissions: ["can_execute"],
|
|
457
478
|
resources: ["http"],
|
|
458
479
|
conditions
|
|
459
|
-
}
|
|
460
|
-
return
|
|
480
|
+
};
|
|
481
|
+
return context.connection_id ? [buildAuthStatement(context.connection_id, idPrefix), statement] : [statement];
|
|
461
482
|
}
|
|
462
|
-
function
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
statements.push(buildAuthStatement(context.connection_id));
|
|
466
|
-
}
|
|
467
|
-
statements.push({
|
|
483
|
+
function actionContextToTier(context, idPrefix) {
|
|
484
|
+
const statement = {
|
|
485
|
+
id: `${idPrefix}_${crypto.randomUUID()}`,
|
|
468
486
|
effect: "allow",
|
|
469
487
|
permissions: ["can_execute"],
|
|
470
488
|
resources: [context.resource],
|
|
471
489
|
conditions: buildExactActionConditions(context)
|
|
472
|
-
}
|
|
473
|
-
return
|
|
490
|
+
};
|
|
491
|
+
return context.connection_id ? [buildAuthStatement(context.connection_id, idPrefix), statement] : [statement];
|
|
474
492
|
}
|
|
475
493
|
function toExactPolicy(context) {
|
|
476
|
-
|
|
477
|
-
|
|
494
|
+
return {
|
|
495
|
+
version: 4,
|
|
496
|
+
rules: [
|
|
497
|
+
context.request_type === "http_request" ? httpContextToTier(context, "r") : actionContextToTier(context, "r")
|
|
498
|
+
]
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function toExactPolicyV3(context) {
|
|
502
|
+
return {
|
|
503
|
+
version: 3,
|
|
504
|
+
statements: [
|
|
505
|
+
context.request_type === "http_request" ? httpContextToTier(context, "s") : actionContextToTier(context, "s")
|
|
506
|
+
]
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/zapier-policy.ts
|
|
511
|
+
var import_policy = require("@zapier/policy");
|
|
512
|
+
var import_policy_schema = require("@zapier/policy-schema");
|
|
513
|
+
var import_zod3 = require("zod");
|
|
514
|
+
var SELECTED_API_GLOB_PART_PATTERN = /^[A-Za-z0-9._+*-]+$/;
|
|
515
|
+
var ACTION_KEY_GLOB_PART_PATTERN = /^[A-Za-z0-9._+:*-]+$/;
|
|
516
|
+
var PERMISSIONS = /* @__PURE__ */ new Set([
|
|
517
|
+
"can_execute",
|
|
518
|
+
"can_use",
|
|
519
|
+
"can_view",
|
|
520
|
+
"can_edit",
|
|
521
|
+
"*"
|
|
522
|
+
]);
|
|
523
|
+
var EFFECT_PRECEDENCE = { allow: 0, ask: 1, deny: 2 };
|
|
524
|
+
function isSelectedApiPattern(value) {
|
|
525
|
+
return isGlobSegment(
|
|
526
|
+
value,
|
|
527
|
+
ACTION_RESOURCE_SEGMENT_PATTERN,
|
|
528
|
+
SELECTED_API_GLOB_PART_PATTERN
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
function isActionKeyPattern(value) {
|
|
532
|
+
return isGlobSegment(
|
|
533
|
+
value,
|
|
534
|
+
ACTION_KEY_SEGMENT_PATTERN,
|
|
535
|
+
ACTION_KEY_GLOB_PART_PATTERN
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
function isGlobSegment(value, exactPattern, globPartPattern) {
|
|
539
|
+
if (exactPattern.test(value)) {
|
|
540
|
+
return true;
|
|
541
|
+
}
|
|
542
|
+
if (!value.includes("*") || value.includes("**")) {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
const parts = value.split("@");
|
|
546
|
+
return parts.length <= 2 && parts.every((part) => globPartPattern.test(part));
|
|
547
|
+
}
|
|
548
|
+
function isActionResourcePattern(value) {
|
|
549
|
+
const segments = value.split("/");
|
|
550
|
+
if (segments.length !== 4 || segments[0] !== "action") {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
const [, selectedApi, actionType, actionKey] = segments;
|
|
554
|
+
return selectedApi !== void 0 && actionType !== void 0 && actionKey !== void 0 && isSelectedApiPattern(selectedApi) && (actionType === "*" || ActionTypeSchema.safeParse(actionType).success) && isActionKeyPattern(actionKey);
|
|
555
|
+
}
|
|
556
|
+
var ActionResourcePatternSchema = import_zod3.z.string().refine(isActionResourcePattern, {
|
|
557
|
+
message: "Expected action/{selected_api}/{canonical_action_type|*}/{action_key_pattern}"
|
|
558
|
+
});
|
|
559
|
+
function resourceFamily(resource) {
|
|
560
|
+
if (resource === "*" || resource === "http") {
|
|
561
|
+
return resource;
|
|
478
562
|
}
|
|
479
|
-
|
|
563
|
+
if (ActionResourcePatternSchema.safeParse(resource).success) {
|
|
564
|
+
return "action";
|
|
565
|
+
}
|
|
566
|
+
if (CONNECTION_RESOURCE_PATTERN.test(resource)) {
|
|
567
|
+
return "connection";
|
|
568
|
+
}
|
|
569
|
+
return void 0;
|
|
480
570
|
}
|
|
571
|
+
function permissionAppliesTo(permission, family) {
|
|
572
|
+
if (permission === "*" || family === "*") {
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
return permission === "can_execute" ? family === "action" || family === "http" : family === "connection";
|
|
576
|
+
}
|
|
577
|
+
function statementGroups(policy) {
|
|
578
|
+
if (policy.version === 4) {
|
|
579
|
+
return policy.rules.map((rules, tierIndex) => ({
|
|
580
|
+
statements: rules,
|
|
581
|
+
path: ["rules", tierIndex],
|
|
582
|
+
entry: "rule"
|
|
583
|
+
}));
|
|
584
|
+
}
|
|
585
|
+
if (policy.version === 3) {
|
|
586
|
+
return policy.statements.map((statements, tierIndex) => ({
|
|
587
|
+
statements,
|
|
588
|
+
path: ["statements", tierIndex],
|
|
589
|
+
entry: "statement"
|
|
590
|
+
}));
|
|
591
|
+
}
|
|
592
|
+
return [
|
|
593
|
+
{
|
|
594
|
+
statements: policy.statements,
|
|
595
|
+
path: ["statements"],
|
|
596
|
+
entry: "statement"
|
|
597
|
+
}
|
|
598
|
+
];
|
|
599
|
+
}
|
|
600
|
+
var ZapierPolicySchema = import_policy_schema.PolicySchema.superRefine((policy, ctx) => {
|
|
601
|
+
statementGroups(policy).forEach(({ statements, path, entry }) => {
|
|
602
|
+
const Entry = entry === "rule" ? "Rule" : "Statement";
|
|
603
|
+
statements.forEach((statement, statementIndex) => {
|
|
604
|
+
const families = statement.resources.map((resource, resourceIndex) => {
|
|
605
|
+
const family = resourceFamily(resource);
|
|
606
|
+
if (family === void 0) {
|
|
607
|
+
ctx.addIssue({
|
|
608
|
+
code: "custom",
|
|
609
|
+
message: 'Expected "http", "connection/{id|*}", a valid action pattern, or "*"',
|
|
610
|
+
path: [...path, statementIndex, "resources", resourceIndex]
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
return family;
|
|
614
|
+
});
|
|
615
|
+
statement.permissions.forEach((permission, permissionIndex) => {
|
|
616
|
+
if (!PERMISSIONS.has(permission)) {
|
|
617
|
+
ctx.addIssue({
|
|
618
|
+
code: "custom",
|
|
619
|
+
message: `Unknown Zapier permission "${permission}"`,
|
|
620
|
+
path: [...path, statementIndex, "permissions", permissionIndex]
|
|
621
|
+
});
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
const incompatibleFamily = families.find(
|
|
625
|
+
(family) => family !== void 0 && !permissionAppliesTo(permission, family)
|
|
626
|
+
);
|
|
627
|
+
if (incompatibleFamily !== void 0) {
|
|
628
|
+
ctx.addIssue({
|
|
629
|
+
code: "custom",
|
|
630
|
+
message: `Permission "${permission}" cannot apply to ${incompatibleFamily} resources`,
|
|
631
|
+
path: [...path, statementIndex, "permissions", permissionIndex]
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
const maskingStatementIndex = statements.findIndex(
|
|
636
|
+
(maskingStatement, maskingIndex) => maskingIndex !== statementIndex && EFFECT_PRECEDENCE[maskingStatement.effect] > EFFECT_PRECEDENCE[statement.effect] && (0, import_policy.isStatementSubsumedBy)(statement, {
|
|
637
|
+
...maskingStatement,
|
|
638
|
+
effect: statement.effect
|
|
639
|
+
})
|
|
640
|
+
);
|
|
641
|
+
if (maskingStatementIndex !== -1) {
|
|
642
|
+
const maskingStatement = statements[maskingStatementIndex];
|
|
643
|
+
const maskingId = maskingStatement.id ? ` (id "${maskingStatement.id}")` : "";
|
|
644
|
+
ctx.addIssue({
|
|
645
|
+
code: "custom",
|
|
646
|
+
message: `${Entry} is entirely masked by higher-precedence ${entry} at index ${maskingStatementIndex}${maskingId}`,
|
|
647
|
+
path: [...path, statementIndex]
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
});
|
|
481
653
|
|
|
482
654
|
// src/connection-policy.ts
|
|
483
|
-
var
|
|
484
|
-
var AccessLevelSchema =
|
|
655
|
+
var import_zod4 = require("zod");
|
|
656
|
+
var AccessLevelSchema = import_zod4.z.enum(["read", "read_write"]);
|
|
657
|
+
function requireAccessLevel(accessLevel) {
|
|
658
|
+
if (!AccessLevelSchema.safeParse(accessLevel).success) {
|
|
659
|
+
throw new Error(
|
|
660
|
+
`invalid access_level "${accessLevel}": expected "read" or "read_write"`
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
485
664
|
var READ_HTTP_METHODS = ["GET", "HEAD", "OPTIONS"];
|
|
486
665
|
var READ_ACTION_TYPES = [
|
|
487
666
|
"read",
|
|
@@ -501,7 +680,19 @@ function isWriteRequest(context) {
|
|
|
501
680
|
}
|
|
502
681
|
function buildConnectionPolicy(options) {
|
|
503
682
|
const { connectionId, selectedApi, accessLevel } = options;
|
|
683
|
+
requireAccessLevel(accessLevel);
|
|
684
|
+
if (!CONNECTION_ID_PATTERN.test(connectionId)) {
|
|
685
|
+
throw new Error(
|
|
686
|
+
`invalid connection_id "${connectionId}": must not contain "/", "*", or whitespace`
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
if (!ACTION_RESOURCE_SEGMENT_PATTERN.test(selectedApi)) {
|
|
690
|
+
throw new Error(
|
|
691
|
+
`invalid selected_api "${selectedApi}": must not contain "/", "*", or whitespace`
|
|
692
|
+
);
|
|
693
|
+
}
|
|
504
694
|
const authStatement = {
|
|
695
|
+
id: `r_${crypto.randomUUID()}`,
|
|
505
696
|
effect: "allow",
|
|
506
697
|
permissions: ["can_use"],
|
|
507
698
|
resources: [`connection/${connectionId}`],
|
|
@@ -521,6 +712,7 @@ function buildConnectionPolicy(options) {
|
|
|
521
712
|
});
|
|
522
713
|
}
|
|
523
714
|
const httpStatement = {
|
|
715
|
+
id: `r_${crypto.randomUUID()}`,
|
|
524
716
|
effect: "allow",
|
|
525
717
|
permissions: ["can_execute"],
|
|
526
718
|
resources: ["http"],
|
|
@@ -529,20 +721,22 @@ function buildConnectionPolicy(options) {
|
|
|
529
721
|
const apiPattern = globSelectedApiVersion(selectedApi);
|
|
530
722
|
const actionResources = accessLevel === "read" ? READ_ACTION_TYPES.map((t) => `action/${apiPattern}/${t}/*`) : [`action/${apiPattern}/*/*`];
|
|
531
723
|
const actionStatement = {
|
|
724
|
+
id: `r_${crypto.randomUUID()}`,
|
|
532
725
|
effect: "allow",
|
|
533
726
|
permissions: ["can_execute"],
|
|
534
727
|
resources: actionResources,
|
|
535
728
|
conditions: [connectionCondition]
|
|
536
729
|
};
|
|
537
730
|
return {
|
|
538
|
-
version:
|
|
539
|
-
|
|
731
|
+
version: 4,
|
|
732
|
+
rules: [[authStatement, httpStatement, actionStatement]]
|
|
540
733
|
};
|
|
541
734
|
}
|
|
542
735
|
|
|
543
736
|
// src/connectionless-policy.ts
|
|
544
737
|
function buildConnectionlessPolicy(options) {
|
|
545
738
|
const { context, accessLevel } = options;
|
|
739
|
+
requireAccessLevel(accessLevel);
|
|
546
740
|
if (context.request_type === "http_request") {
|
|
547
741
|
const conditions = [
|
|
548
742
|
{
|
|
@@ -559,27 +753,36 @@ function buildConnectionlessPolicy(options) {
|
|
|
559
753
|
});
|
|
560
754
|
}
|
|
561
755
|
const statement2 = {
|
|
756
|
+
id: `r_${crypto.randomUUID()}`,
|
|
562
757
|
effect: "allow",
|
|
563
758
|
permissions: ["can_execute"],
|
|
564
759
|
resources: ["http"],
|
|
565
760
|
conditions
|
|
566
761
|
};
|
|
567
|
-
return {
|
|
762
|
+
return {
|
|
763
|
+
version: 4,
|
|
764
|
+
rules: [[statement2]]
|
|
765
|
+
};
|
|
568
766
|
}
|
|
569
767
|
const { selected_api } = parseActionResource(context.resource);
|
|
570
768
|
const apiPattern = globSelectedApiVersion(selected_api);
|
|
571
769
|
const resources = accessLevel === "read" ? READ_ACTION_TYPES.map((t) => `action/${apiPattern}/${t}/*`) : [`action/${apiPattern}/*/*`];
|
|
572
770
|
const statement = {
|
|
771
|
+
id: `r_${crypto.randomUUID()}`,
|
|
573
772
|
effect: "allow",
|
|
574
773
|
permissions: ["can_execute"],
|
|
575
774
|
resources,
|
|
576
775
|
conditions: []
|
|
577
776
|
};
|
|
578
|
-
return {
|
|
777
|
+
return {
|
|
778
|
+
version: 4,
|
|
779
|
+
rules: [[statement]]
|
|
780
|
+
};
|
|
579
781
|
}
|
|
580
782
|
// Annotate the CommonJS export names for ESM import in node:
|
|
581
783
|
0 && (module.exports = {
|
|
582
784
|
AccessLevelSchema,
|
|
785
|
+
ActionResourcePatternSchema,
|
|
583
786
|
ActionRunContextInputSchema,
|
|
584
787
|
ActionRunContextSchema,
|
|
585
788
|
ActionTypeSchema,
|
|
@@ -592,6 +795,7 @@ function buildConnectionlessPolicy(options) {
|
|
|
592
795
|
READ_ACTION_TYPES,
|
|
593
796
|
READ_HTTP_METHODS,
|
|
594
797
|
RequestContextSchema,
|
|
798
|
+
ZapierPolicySchema,
|
|
595
799
|
buildActionResource,
|
|
596
800
|
buildActionRunContext,
|
|
597
801
|
buildBroadActionResource,
|
|
@@ -608,6 +812,7 @@ function buildConnectionlessPolicy(options) {
|
|
|
608
812
|
parseActionResource,
|
|
609
813
|
parseUrl,
|
|
610
814
|
toAppKey,
|
|
611
|
-
toExactPolicy
|
|
815
|
+
toExactPolicy,
|
|
816
|
+
toExactPolicyV3
|
|
612
817
|
});
|
|
613
818
|
//# sourceMappingURL=index.cjs.map
|