@zapier/policy-context 1.3.0 → 3.0.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/README.md +24 -2
- package/dist/index.cjs +223 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +506 -9
- package/dist/index.d.ts +506 -9
- package/dist/index.js +220 -33
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
// src/types.ts
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
function containsUnsafeInteger(value) {
|
|
4
|
+
if (typeof value === "number") {
|
|
5
|
+
return Number.isInteger(value) && !Number.isSafeInteger(value);
|
|
6
|
+
}
|
|
7
|
+
if (Array.isArray(value)) {
|
|
8
|
+
return value.some(containsUnsafeInteger);
|
|
9
|
+
}
|
|
10
|
+
if (value !== null && typeof value === "object") {
|
|
11
|
+
return Object.values(value).some(containsUnsafeInteger);
|
|
12
|
+
}
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
var SafeJsonValueSchema = z.json().refine((value) => !containsUnsafeInteger(value), {
|
|
16
|
+
message: "JSON integers must be within the IEEE-754 safe range; encode larger identifiers as strings"
|
|
17
|
+
});
|
|
3
18
|
var ParsedUrlSchema = z.object({
|
|
4
19
|
scheme: z.string().describe('URL scheme, e.g. "https"'),
|
|
5
20
|
host: z.string().describe('Hostname, e.g. "api.example.com"'),
|
|
@@ -58,7 +73,7 @@ var HttpRequestContextSchema = z.object({
|
|
|
58
73
|
* `z.json()` still rejects non-JSON values (`Date`, `undefined`,
|
|
59
74
|
* functions) at parse time. Same inferred `JsonValue` type either way.
|
|
60
75
|
*/
|
|
61
|
-
body_json: z.unknown().pipe(
|
|
76
|
+
body_json: z.unknown().pipe(SafeJsonValueSchema).nullable().describe("Parsed JSON body (if applicable)"),
|
|
62
77
|
/** Zapier connection ID used for this request */
|
|
63
78
|
connection_id: z.string().max(255).optional().describe("Zapier connection ID used for this request"),
|
|
64
79
|
/** Optional human-readable reason for the request */
|
|
@@ -76,6 +91,11 @@ var ActionTypeSchema = z.enum([
|
|
|
76
91
|
]).meta({ id: "action_type", description: "Canonical Zapier action type." });
|
|
77
92
|
var ACTION_RESOURCE_SEGMENT_PATTERN = /^[A-Za-z0-9._+-]+(?:@[A-Za-z0-9._+-]+)?$/;
|
|
78
93
|
var ACTION_KEY_SEGMENT_PATTERN = /^[A-Za-z0-9._+:-]+(?:@[A-Za-z0-9._+:-]+)?$/;
|
|
94
|
+
var CONNECTION_ID_CHARS = "[^/*\\x00-\\x20\\x7f-\\xa0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]";
|
|
95
|
+
var CONNECTION_ID_PATTERN = new RegExp(`^${CONNECTION_ID_CHARS}+$`);
|
|
96
|
+
var CONNECTION_RESOURCE_PATTERN = new RegExp(
|
|
97
|
+
`^connection/(?:\\*|${CONNECTION_ID_CHARS}+)$`
|
|
98
|
+
);
|
|
79
99
|
function parseActionResource(resource) {
|
|
80
100
|
const segments = resource.split("/");
|
|
81
101
|
if (segments.length !== 4 || segments[0] !== "action") {
|
|
@@ -136,7 +156,7 @@ var ActionRunContextInputSchema = z.object({
|
|
|
136
156
|
}),
|
|
137
157
|
connection_id: z.string().max(255).optional().describe("Zapier connection ID used for this request"),
|
|
138
158
|
/** Action input parameters. See body_json above for the pipe rationale. */
|
|
139
|
-
inputs: z.unknown().pipe(
|
|
159
|
+
inputs: z.unknown().pipe(SafeJsonValueSchema).optional().describe("Action input parameters"),
|
|
140
160
|
reason: z.string().max(1024).optional().describe("Optional human-readable reason for the request")
|
|
141
161
|
}).meta({
|
|
142
162
|
id: "ActionRunContext",
|
|
@@ -167,7 +187,7 @@ var ActionRunContextSchema = ActionRunContextInputSchema.transform(
|
|
|
167
187
|
...input.connection_id !== void 0 && {
|
|
168
188
|
connection_id: input.connection_id
|
|
169
189
|
},
|
|
170
|
-
...input.inputs !== void 0 && { inputs: input.inputs },
|
|
190
|
+
...input.inputs !== void 0 && input.inputs !== null && { inputs: input.inputs },
|
|
171
191
|
...input.reason !== void 0 && { reason: input.reason }
|
|
172
192
|
};
|
|
173
193
|
}
|
|
@@ -211,8 +231,15 @@ var IGNORED_HEADERS = /* @__PURE__ */ new Set([
|
|
|
211
231
|
"x-real-ip",
|
|
212
232
|
// Proxy internal — sdkapi-specific headers
|
|
213
233
|
"x-is-interactive-session",
|
|
214
|
-
"x-relay-callback-url"
|
|
234
|
+
"x-relay-callback-url",
|
|
235
|
+
// Subsumed by isIgnoredHeader's x-zapier- prefix rule; kept for byte-parity
|
|
236
|
+
// with the approvalsapi source and for consumers reading the set directly.
|
|
237
|
+
"x-zapier-approval-token"
|
|
215
238
|
]);
|
|
239
|
+
function isIgnoredHeader(name) {
|
|
240
|
+
const normalized = name.toLowerCase();
|
|
241
|
+
return IGNORED_HEADERS.has(normalized) || normalized.startsWith("zapier-") || normalized.startsWith("x-zapier-");
|
|
242
|
+
}
|
|
216
243
|
|
|
217
244
|
// src/builders.ts
|
|
218
245
|
import { z as z2 } from "zod";
|
|
@@ -235,14 +262,14 @@ function buildExactHttpConditions(context) {
|
|
|
235
262
|
conditions.push({
|
|
236
263
|
path: ["url", "query", key],
|
|
237
264
|
operator: "equals",
|
|
238
|
-
value: values
|
|
265
|
+
value: structuredClone(values)
|
|
239
266
|
});
|
|
240
267
|
}
|
|
241
268
|
if (context.body_json !== null) {
|
|
242
269
|
conditions.push({
|
|
243
270
|
path: ["body_json"],
|
|
244
271
|
operator: "equals",
|
|
245
|
-
value: context.body_json
|
|
272
|
+
value: structuredClone(context.body_json)
|
|
246
273
|
});
|
|
247
274
|
} else if (context.body !== null) {
|
|
248
275
|
conditions.push({
|
|
@@ -252,7 +279,7 @@ function buildExactHttpConditions(context) {
|
|
|
252
279
|
});
|
|
253
280
|
}
|
|
254
281
|
for (const [key, value] of Object.entries(context.headers)) {
|
|
255
|
-
if (!
|
|
282
|
+
if (!isIgnoredHeader(key) && typeof value === "string") {
|
|
256
283
|
conditions.push({
|
|
257
284
|
path: ["headers", key.toLowerCase()],
|
|
258
285
|
operator: "equals",
|
|
@@ -277,7 +304,7 @@ function buildExactActionConditions(context) {
|
|
|
277
304
|
conditions.push({
|
|
278
305
|
path: ["inputs"],
|
|
279
306
|
operator: "equals",
|
|
280
|
-
value: context.inputs
|
|
307
|
+
value: structuredClone(context.inputs)
|
|
281
308
|
});
|
|
282
309
|
}
|
|
283
310
|
return conditions;
|
|
@@ -294,7 +321,7 @@ var BuildHttpRequestContextOptionsSchema = z2.object({
|
|
|
294
321
|
method: z2.string(),
|
|
295
322
|
/** Raw URL string. Will be normalized with https:// if no protocol present. */
|
|
296
323
|
url: z2.string(),
|
|
297
|
-
/** Raw headers from the request. Will be filtered through
|
|
324
|
+
/** Raw headers from the request. Will be filtered through isIgnoredHeader. */
|
|
298
325
|
headers: z2.record(
|
|
299
326
|
z2.string(),
|
|
300
327
|
z2.union([z2.string(), z2.array(z2.string()), z2.undefined()])
|
|
@@ -302,7 +329,7 @@ var BuildHttpRequestContextOptionsSchema = z2.object({
|
|
|
302
329
|
/** Raw body string */
|
|
303
330
|
body: z2.string().nullable().optional(),
|
|
304
331
|
/** Pre-parsed JSON body (e.g. from Fastify's automatic JSON parsing) */
|
|
305
|
-
body_json:
|
|
332
|
+
body_json: SafeJsonValueSchema.optional(),
|
|
306
333
|
/** Connection ID associated with this request */
|
|
307
334
|
connection_id: z2.string().optional()
|
|
308
335
|
});
|
|
@@ -314,7 +341,7 @@ function buildHttpRequestContext(options) {
|
|
|
314
341
|
if (parsed.headers) {
|
|
315
342
|
for (const [key, value] of Object.entries(parsed.headers)) {
|
|
316
343
|
const lowerKey = key.toLowerCase();
|
|
317
|
-
if (!
|
|
344
|
+
if (!isIgnoredHeader(lowerKey) && typeof value === "string") {
|
|
318
345
|
headers[lowerKey] = value;
|
|
319
346
|
}
|
|
320
347
|
}
|
|
@@ -337,7 +364,7 @@ function buildHttpRequestContext(options) {
|
|
|
337
364
|
method: parsed.method.toUpperCase(),
|
|
338
365
|
url,
|
|
339
366
|
headers,
|
|
340
|
-
body: parsed.body ?? null,
|
|
367
|
+
body: body_json === null ? parsed.body ?? null : null,
|
|
341
368
|
body_json,
|
|
342
369
|
connection_id: parsed.connection_id
|
|
343
370
|
});
|
|
@@ -351,7 +378,7 @@ var BuildActionRunContextOptionsSchema = z2.object({
|
|
|
351
378
|
message: "action_key must contain only [A-Za-z0-9._@:+-] (no globs, slashes, or whitespace)"
|
|
352
379
|
}),
|
|
353
380
|
connection_id: z2.string().optional(),
|
|
354
|
-
inputs:
|
|
381
|
+
inputs: SafeJsonValueSchema.optional(),
|
|
355
382
|
reason: z2.string().optional()
|
|
356
383
|
});
|
|
357
384
|
function buildActionRunContext(options) {
|
|
@@ -372,6 +399,7 @@ function buildActionRunContext(options) {
|
|
|
372
399
|
// src/policies.ts
|
|
373
400
|
function buildAuthStatement(authenticationId) {
|
|
374
401
|
return {
|
|
402
|
+
id: `s_${crypto.randomUUID()}`,
|
|
375
403
|
effect: "allow",
|
|
376
404
|
permissions: ["can_use"],
|
|
377
405
|
resources: [`connection/${authenticationId}`],
|
|
@@ -379,36 +407,38 @@ function buildAuthStatement(authenticationId) {
|
|
|
379
407
|
};
|
|
380
408
|
}
|
|
381
409
|
function httpContextToPolicy(context) {
|
|
382
|
-
const statements = [];
|
|
383
|
-
if (context.connection_id) {
|
|
384
|
-
statements.push(buildAuthStatement(context.connection_id));
|
|
385
|
-
}
|
|
386
410
|
const conditions = buildExactHttpConditions(context);
|
|
387
411
|
conditions.push({
|
|
388
412
|
path: ["url", "query"],
|
|
389
413
|
operator: "keys_in",
|
|
390
414
|
value: Object.keys(context.url.query)
|
|
391
415
|
});
|
|
392
|
-
|
|
416
|
+
const statement = {
|
|
417
|
+
id: `s_${crypto.randomUUID()}`,
|
|
393
418
|
effect: "allow",
|
|
394
419
|
permissions: ["can_execute"],
|
|
395
420
|
resources: ["http"],
|
|
396
421
|
conditions
|
|
397
|
-
}
|
|
398
|
-
|
|
422
|
+
};
|
|
423
|
+
const statements = context.connection_id ? [buildAuthStatement(context.connection_id), statement] : [statement];
|
|
424
|
+
return {
|
|
425
|
+
version: 3,
|
|
426
|
+
statements: [statements]
|
|
427
|
+
};
|
|
399
428
|
}
|
|
400
429
|
function actionContextToPolicy(context) {
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
statements.push(buildAuthStatement(context.connection_id));
|
|
404
|
-
}
|
|
405
|
-
statements.push({
|
|
430
|
+
const statement = {
|
|
431
|
+
id: `s_${crypto.randomUUID()}`,
|
|
406
432
|
effect: "allow",
|
|
407
433
|
permissions: ["can_execute"],
|
|
408
434
|
resources: [context.resource],
|
|
409
435
|
conditions: buildExactActionConditions(context)
|
|
410
|
-
}
|
|
411
|
-
|
|
436
|
+
};
|
|
437
|
+
const statements = context.connection_id ? [buildAuthStatement(context.connection_id), statement] : [statement];
|
|
438
|
+
return {
|
|
439
|
+
version: 3,
|
|
440
|
+
statements: [statements]
|
|
441
|
+
};
|
|
412
442
|
}
|
|
413
443
|
function toExactPolicy(context) {
|
|
414
444
|
if (context.request_type === "http_request") {
|
|
@@ -417,9 +447,140 @@ function toExactPolicy(context) {
|
|
|
417
447
|
return actionContextToPolicy(context);
|
|
418
448
|
}
|
|
419
449
|
|
|
420
|
-
// src/
|
|
450
|
+
// src/zapier-policy.ts
|
|
451
|
+
import { isStatementSubsumedBy } from "@zapier/policy";
|
|
452
|
+
import { PolicySchema } from "@zapier/policy-schema";
|
|
421
453
|
import { z as z3 } from "zod";
|
|
422
|
-
var
|
|
454
|
+
var SELECTED_API_GLOB_PART_PATTERN = /^[A-Za-z0-9._+*-]+$/;
|
|
455
|
+
var ACTION_KEY_GLOB_PART_PATTERN = /^[A-Za-z0-9._+:*-]+$/;
|
|
456
|
+
var PERMISSIONS = /* @__PURE__ */ new Set([
|
|
457
|
+
"can_execute",
|
|
458
|
+
"can_use",
|
|
459
|
+
"can_view",
|
|
460
|
+
"can_edit",
|
|
461
|
+
"*"
|
|
462
|
+
]);
|
|
463
|
+
var EFFECT_PRECEDENCE = { allow: 0, ask: 1, deny: 2 };
|
|
464
|
+
function isSelectedApiPattern(value) {
|
|
465
|
+
return isGlobSegment(
|
|
466
|
+
value,
|
|
467
|
+
ACTION_RESOURCE_SEGMENT_PATTERN,
|
|
468
|
+
SELECTED_API_GLOB_PART_PATTERN
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
function isActionKeyPattern(value) {
|
|
472
|
+
return isGlobSegment(
|
|
473
|
+
value,
|
|
474
|
+
ACTION_KEY_SEGMENT_PATTERN,
|
|
475
|
+
ACTION_KEY_GLOB_PART_PATTERN
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
function isGlobSegment(value, exactPattern, globPartPattern) {
|
|
479
|
+
if (exactPattern.test(value)) {
|
|
480
|
+
return true;
|
|
481
|
+
}
|
|
482
|
+
if (!value.includes("*") || value.includes("**")) {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
const parts = value.split("@");
|
|
486
|
+
return parts.length <= 2 && parts.every((part) => globPartPattern.test(part));
|
|
487
|
+
}
|
|
488
|
+
function isActionResourcePattern(value) {
|
|
489
|
+
const segments = value.split("/");
|
|
490
|
+
if (segments.length !== 4 || segments[0] !== "action") {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
const [, selectedApi, actionType, actionKey] = segments;
|
|
494
|
+
return selectedApi !== void 0 && actionType !== void 0 && actionKey !== void 0 && isSelectedApiPattern(selectedApi) && (actionType === "*" || ActionTypeSchema.safeParse(actionType).success) && isActionKeyPattern(actionKey);
|
|
495
|
+
}
|
|
496
|
+
var ActionResourcePatternSchema = z3.string().refine(isActionResourcePattern, {
|
|
497
|
+
message: "Expected action/{selected_api}/{canonical_action_type|*}/{action_key_pattern}"
|
|
498
|
+
});
|
|
499
|
+
function resourceFamily(resource) {
|
|
500
|
+
if (resource === "*" || resource === "http") {
|
|
501
|
+
return resource;
|
|
502
|
+
}
|
|
503
|
+
if (ActionResourcePatternSchema.safeParse(resource).success) {
|
|
504
|
+
return "action";
|
|
505
|
+
}
|
|
506
|
+
if (CONNECTION_RESOURCE_PATTERN.test(resource)) {
|
|
507
|
+
return "connection";
|
|
508
|
+
}
|
|
509
|
+
return void 0;
|
|
510
|
+
}
|
|
511
|
+
function permissionAppliesTo(permission, family) {
|
|
512
|
+
if (permission === "*" || family === "*") {
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
515
|
+
return permission === "can_execute" ? family === "action" || family === "http" : family === "connection";
|
|
516
|
+
}
|
|
517
|
+
var ZapierPolicySchema = PolicySchema.superRefine((policy, ctx) => {
|
|
518
|
+
const tiers = policy.version === 3 ? policy.statements.map((statements, tierIndex) => ({
|
|
519
|
+
statements,
|
|
520
|
+
path: ["statements", tierIndex]
|
|
521
|
+
})) : [{ statements: policy.statements, path: ["statements"] }];
|
|
522
|
+
tiers.forEach(({ statements, path }) => {
|
|
523
|
+
statements.forEach((statement, statementIndex) => {
|
|
524
|
+
const families = statement.resources.map((resource, resourceIndex) => {
|
|
525
|
+
const family = resourceFamily(resource);
|
|
526
|
+
if (family === void 0) {
|
|
527
|
+
ctx.addIssue({
|
|
528
|
+
code: "custom",
|
|
529
|
+
message: 'Expected "http", "connection/{id|*}", a valid action pattern, or "*"',
|
|
530
|
+
path: [...path, statementIndex, "resources", resourceIndex]
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
return family;
|
|
534
|
+
});
|
|
535
|
+
statement.permissions.forEach((permission, permissionIndex) => {
|
|
536
|
+
if (!PERMISSIONS.has(permission)) {
|
|
537
|
+
ctx.addIssue({
|
|
538
|
+
code: "custom",
|
|
539
|
+
message: `Unknown Zapier permission "${permission}"`,
|
|
540
|
+
path: [...path, statementIndex, "permissions", permissionIndex]
|
|
541
|
+
});
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
const incompatibleFamily = families.find(
|
|
545
|
+
(family) => family !== void 0 && !permissionAppliesTo(permission, family)
|
|
546
|
+
);
|
|
547
|
+
if (incompatibleFamily !== void 0) {
|
|
548
|
+
ctx.addIssue({
|
|
549
|
+
code: "custom",
|
|
550
|
+
message: `Permission "${permission}" cannot apply to ${incompatibleFamily} resources`,
|
|
551
|
+
path: [...path, statementIndex, "permissions", permissionIndex]
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
const maskingStatementIndex = statements.findIndex(
|
|
556
|
+
(maskingStatement, maskingIndex) => maskingIndex !== statementIndex && EFFECT_PRECEDENCE[maskingStatement.effect] > EFFECT_PRECEDENCE[statement.effect] && isStatementSubsumedBy(statement, {
|
|
557
|
+
...maskingStatement,
|
|
558
|
+
effect: statement.effect
|
|
559
|
+
})
|
|
560
|
+
);
|
|
561
|
+
if (maskingStatementIndex !== -1) {
|
|
562
|
+
const maskingStatement = statements[maskingStatementIndex];
|
|
563
|
+
const maskingId = maskingStatement.id ? ` (id "${maskingStatement.id}")` : "";
|
|
564
|
+
ctx.addIssue({
|
|
565
|
+
code: "custom",
|
|
566
|
+
message: `Statement is entirely masked by higher-precedence statement at index ${maskingStatementIndex}${maskingId}`,
|
|
567
|
+
path: [...path, statementIndex]
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
// src/connection-policy.ts
|
|
575
|
+
import { z as z4 } from "zod";
|
|
576
|
+
var AccessLevelSchema = z4.enum(["read", "read_write"]);
|
|
577
|
+
function requireAccessLevel(accessLevel) {
|
|
578
|
+
if (!AccessLevelSchema.safeParse(accessLevel).success) {
|
|
579
|
+
throw new Error(
|
|
580
|
+
`invalid access_level "${accessLevel}": expected "read" or "read_write"`
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
423
584
|
var READ_HTTP_METHODS = ["GET", "HEAD", "OPTIONS"];
|
|
424
585
|
var READ_ACTION_TYPES = [
|
|
425
586
|
"read",
|
|
@@ -439,7 +600,19 @@ function isWriteRequest(context) {
|
|
|
439
600
|
}
|
|
440
601
|
function buildConnectionPolicy(options) {
|
|
441
602
|
const { connectionId, selectedApi, accessLevel } = options;
|
|
603
|
+
requireAccessLevel(accessLevel);
|
|
604
|
+
if (!CONNECTION_ID_PATTERN.test(connectionId)) {
|
|
605
|
+
throw new Error(
|
|
606
|
+
`invalid connection_id "${connectionId}": must not contain "/", "*", or whitespace`
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
if (!ACTION_RESOURCE_SEGMENT_PATTERN.test(selectedApi)) {
|
|
610
|
+
throw new Error(
|
|
611
|
+
`invalid selected_api "${selectedApi}": must not contain "/", "*", or whitespace`
|
|
612
|
+
);
|
|
613
|
+
}
|
|
442
614
|
const authStatement = {
|
|
615
|
+
id: `s_${crypto.randomUUID()}`,
|
|
443
616
|
effect: "allow",
|
|
444
617
|
permissions: ["can_use"],
|
|
445
618
|
resources: [`connection/${connectionId}`],
|
|
@@ -459,6 +632,7 @@ function buildConnectionPolicy(options) {
|
|
|
459
632
|
});
|
|
460
633
|
}
|
|
461
634
|
const httpStatement = {
|
|
635
|
+
id: `s_${crypto.randomUUID()}`,
|
|
462
636
|
effect: "allow",
|
|
463
637
|
permissions: ["can_execute"],
|
|
464
638
|
resources: ["http"],
|
|
@@ -467,20 +641,22 @@ function buildConnectionPolicy(options) {
|
|
|
467
641
|
const apiPattern = globSelectedApiVersion(selectedApi);
|
|
468
642
|
const actionResources = accessLevel === "read" ? READ_ACTION_TYPES.map((t) => `action/${apiPattern}/${t}/*`) : [`action/${apiPattern}/*/*`];
|
|
469
643
|
const actionStatement = {
|
|
644
|
+
id: `s_${crypto.randomUUID()}`,
|
|
470
645
|
effect: "allow",
|
|
471
646
|
permissions: ["can_execute"],
|
|
472
647
|
resources: actionResources,
|
|
473
648
|
conditions: [connectionCondition]
|
|
474
649
|
};
|
|
475
650
|
return {
|
|
476
|
-
version:
|
|
477
|
-
statements: [authStatement, httpStatement, actionStatement]
|
|
651
|
+
version: 3,
|
|
652
|
+
statements: [[authStatement, httpStatement, actionStatement]]
|
|
478
653
|
};
|
|
479
654
|
}
|
|
480
655
|
|
|
481
656
|
// src/connectionless-policy.ts
|
|
482
657
|
function buildConnectionlessPolicy(options) {
|
|
483
658
|
const { context, accessLevel } = options;
|
|
659
|
+
requireAccessLevel(accessLevel);
|
|
484
660
|
if (context.request_type === "http_request") {
|
|
485
661
|
const conditions = [
|
|
486
662
|
{
|
|
@@ -497,26 +673,35 @@ function buildConnectionlessPolicy(options) {
|
|
|
497
673
|
});
|
|
498
674
|
}
|
|
499
675
|
const statement2 = {
|
|
676
|
+
id: `s_${crypto.randomUUID()}`,
|
|
500
677
|
effect: "allow",
|
|
501
678
|
permissions: ["can_execute"],
|
|
502
679
|
resources: ["http"],
|
|
503
680
|
conditions
|
|
504
681
|
};
|
|
505
|
-
return {
|
|
682
|
+
return {
|
|
683
|
+
version: 3,
|
|
684
|
+
statements: [[statement2]]
|
|
685
|
+
};
|
|
506
686
|
}
|
|
507
687
|
const { selected_api } = parseActionResource(context.resource);
|
|
508
688
|
const apiPattern = globSelectedApiVersion(selected_api);
|
|
509
689
|
const resources = accessLevel === "read" ? READ_ACTION_TYPES.map((t) => `action/${apiPattern}/${t}/*`) : [`action/${apiPattern}/*/*`];
|
|
510
690
|
const statement = {
|
|
691
|
+
id: `s_${crypto.randomUUID()}`,
|
|
511
692
|
effect: "allow",
|
|
512
693
|
permissions: ["can_execute"],
|
|
513
694
|
resources,
|
|
514
695
|
conditions: []
|
|
515
696
|
};
|
|
516
|
-
return {
|
|
697
|
+
return {
|
|
698
|
+
version: 3,
|
|
699
|
+
statements: [[statement]]
|
|
700
|
+
};
|
|
517
701
|
}
|
|
518
702
|
export {
|
|
519
703
|
AccessLevelSchema,
|
|
704
|
+
ActionResourcePatternSchema,
|
|
520
705
|
ActionRunContextInputSchema,
|
|
521
706
|
ActionRunContextSchema,
|
|
522
707
|
ActionTypeSchema,
|
|
@@ -529,6 +714,7 @@ export {
|
|
|
529
714
|
READ_ACTION_TYPES,
|
|
530
715
|
READ_HTTP_METHODS,
|
|
531
716
|
RequestContextSchema,
|
|
717
|
+
ZapierPolicySchema,
|
|
532
718
|
buildActionResource,
|
|
533
719
|
buildActionRunContext,
|
|
534
720
|
buildBroadActionResource,
|
|
@@ -540,6 +726,7 @@ export {
|
|
|
540
726
|
buildHttpRequestContext,
|
|
541
727
|
buildUrlString,
|
|
542
728
|
globSelectedApiVersion,
|
|
729
|
+
isIgnoredHeader,
|
|
543
730
|
isWriteRequest,
|
|
544
731
|
parseActionResource,
|
|
545
732
|
parseUrl,
|