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