@zapier/policy-context 1.3.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 +243 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +735 -9
- package/dist/index.d.ts +735 -9
- package/dist/index.js +242 -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: `r_${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: `r_${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: 4,
|
|
426
|
+
rules: [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: `r_${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: 4,
|
|
440
|
+
rules: [statements]
|
|
441
|
+
};
|
|
412
442
|
}
|
|
413
443
|
function toExactPolicy(context) {
|
|
414
444
|
if (context.request_type === "http_request") {
|
|
@@ -417,9 +447,162 @@ 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 {
|
|
453
|
+
PolicySchema
|
|
454
|
+
} from "@zapier/policy-schema";
|
|
421
455
|
import { z as z3 } from "zod";
|
|
422
|
-
var
|
|
456
|
+
var SELECTED_API_GLOB_PART_PATTERN = /^[A-Za-z0-9._+*-]+$/;
|
|
457
|
+
var ACTION_KEY_GLOB_PART_PATTERN = /^[A-Za-z0-9._+:*-]+$/;
|
|
458
|
+
var PERMISSIONS = /* @__PURE__ */ new Set([
|
|
459
|
+
"can_execute",
|
|
460
|
+
"can_use",
|
|
461
|
+
"can_view",
|
|
462
|
+
"can_edit",
|
|
463
|
+
"*"
|
|
464
|
+
]);
|
|
465
|
+
var EFFECT_PRECEDENCE = { allow: 0, ask: 1, deny: 2 };
|
|
466
|
+
function isSelectedApiPattern(value) {
|
|
467
|
+
return isGlobSegment(
|
|
468
|
+
value,
|
|
469
|
+
ACTION_RESOURCE_SEGMENT_PATTERN,
|
|
470
|
+
SELECTED_API_GLOB_PART_PATTERN
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
function isActionKeyPattern(value) {
|
|
474
|
+
return isGlobSegment(
|
|
475
|
+
value,
|
|
476
|
+
ACTION_KEY_SEGMENT_PATTERN,
|
|
477
|
+
ACTION_KEY_GLOB_PART_PATTERN
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
function isGlobSegment(value, exactPattern, globPartPattern) {
|
|
481
|
+
if (exactPattern.test(value)) {
|
|
482
|
+
return true;
|
|
483
|
+
}
|
|
484
|
+
if (!value.includes("*") || value.includes("**")) {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
const parts = value.split("@");
|
|
488
|
+
return parts.length <= 2 && parts.every((part) => globPartPattern.test(part));
|
|
489
|
+
}
|
|
490
|
+
function isActionResourcePattern(value) {
|
|
491
|
+
const segments = value.split("/");
|
|
492
|
+
if (segments.length !== 4 || segments[0] !== "action") {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
const [, selectedApi, actionType, actionKey] = segments;
|
|
496
|
+
return selectedApi !== void 0 && actionType !== void 0 && actionKey !== void 0 && isSelectedApiPattern(selectedApi) && (actionType === "*" || ActionTypeSchema.safeParse(actionType).success) && isActionKeyPattern(actionKey);
|
|
497
|
+
}
|
|
498
|
+
var ActionResourcePatternSchema = z3.string().refine(isActionResourcePattern, {
|
|
499
|
+
message: "Expected action/{selected_api}/{canonical_action_type|*}/{action_key_pattern}"
|
|
500
|
+
});
|
|
501
|
+
function resourceFamily(resource) {
|
|
502
|
+
if (resource === "*" || resource === "http") {
|
|
503
|
+
return resource;
|
|
504
|
+
}
|
|
505
|
+
if (ActionResourcePatternSchema.safeParse(resource).success) {
|
|
506
|
+
return "action";
|
|
507
|
+
}
|
|
508
|
+
if (CONNECTION_RESOURCE_PATTERN.test(resource)) {
|
|
509
|
+
return "connection";
|
|
510
|
+
}
|
|
511
|
+
return void 0;
|
|
512
|
+
}
|
|
513
|
+
function permissionAppliesTo(permission, family) {
|
|
514
|
+
if (permission === "*" || family === "*") {
|
|
515
|
+
return true;
|
|
516
|
+
}
|
|
517
|
+
return permission === "can_execute" ? family === "action" || family === "http" : family === "connection";
|
|
518
|
+
}
|
|
519
|
+
function statementGroups(policy) {
|
|
520
|
+
if (policy.version === 4) {
|
|
521
|
+
return policy.rules.map((rules, tierIndex) => ({
|
|
522
|
+
statements: rules,
|
|
523
|
+
path: ["rules", tierIndex],
|
|
524
|
+
entry: "rule"
|
|
525
|
+
}));
|
|
526
|
+
}
|
|
527
|
+
if (policy.version === 3) {
|
|
528
|
+
return policy.statements.map((statements, tierIndex) => ({
|
|
529
|
+
statements,
|
|
530
|
+
path: ["statements", tierIndex],
|
|
531
|
+
entry: "statement"
|
|
532
|
+
}));
|
|
533
|
+
}
|
|
534
|
+
return [
|
|
535
|
+
{
|
|
536
|
+
statements: policy.statements,
|
|
537
|
+
path: ["statements"],
|
|
538
|
+
entry: "statement"
|
|
539
|
+
}
|
|
540
|
+
];
|
|
541
|
+
}
|
|
542
|
+
var ZapierPolicySchema = PolicySchema.superRefine((policy, ctx) => {
|
|
543
|
+
statementGroups(policy).forEach(({ statements, path, entry }) => {
|
|
544
|
+
const Entry = entry === "rule" ? "Rule" : "Statement";
|
|
545
|
+
statements.forEach((statement, statementIndex) => {
|
|
546
|
+
const families = statement.resources.map((resource, resourceIndex) => {
|
|
547
|
+
const family = resourceFamily(resource);
|
|
548
|
+
if (family === void 0) {
|
|
549
|
+
ctx.addIssue({
|
|
550
|
+
code: "custom",
|
|
551
|
+
message: 'Expected "http", "connection/{id|*}", a valid action pattern, or "*"',
|
|
552
|
+
path: [...path, statementIndex, "resources", resourceIndex]
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
return family;
|
|
556
|
+
});
|
|
557
|
+
statement.permissions.forEach((permission, permissionIndex) => {
|
|
558
|
+
if (!PERMISSIONS.has(permission)) {
|
|
559
|
+
ctx.addIssue({
|
|
560
|
+
code: "custom",
|
|
561
|
+
message: `Unknown Zapier permission "${permission}"`,
|
|
562
|
+
path: [...path, statementIndex, "permissions", permissionIndex]
|
|
563
|
+
});
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
const incompatibleFamily = families.find(
|
|
567
|
+
(family) => family !== void 0 && !permissionAppliesTo(permission, family)
|
|
568
|
+
);
|
|
569
|
+
if (incompatibleFamily !== void 0) {
|
|
570
|
+
ctx.addIssue({
|
|
571
|
+
code: "custom",
|
|
572
|
+
message: `Permission "${permission}" cannot apply to ${incompatibleFamily} resources`,
|
|
573
|
+
path: [...path, statementIndex, "permissions", permissionIndex]
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
const maskingStatementIndex = statements.findIndex(
|
|
578
|
+
(maskingStatement, maskingIndex) => maskingIndex !== statementIndex && EFFECT_PRECEDENCE[maskingStatement.effect] > EFFECT_PRECEDENCE[statement.effect] && isStatementSubsumedBy(statement, {
|
|
579
|
+
...maskingStatement,
|
|
580
|
+
effect: statement.effect
|
|
581
|
+
})
|
|
582
|
+
);
|
|
583
|
+
if (maskingStatementIndex !== -1) {
|
|
584
|
+
const maskingStatement = statements[maskingStatementIndex];
|
|
585
|
+
const maskingId = maskingStatement.id ? ` (id "${maskingStatement.id}")` : "";
|
|
586
|
+
ctx.addIssue({
|
|
587
|
+
code: "custom",
|
|
588
|
+
message: `${Entry} is entirely masked by higher-precedence ${entry} at index ${maskingStatementIndex}${maskingId}`,
|
|
589
|
+
path: [...path, statementIndex]
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// src/connection-policy.ts
|
|
597
|
+
import { z as z4 } from "zod";
|
|
598
|
+
var AccessLevelSchema = z4.enum(["read", "read_write"]);
|
|
599
|
+
function requireAccessLevel(accessLevel) {
|
|
600
|
+
if (!AccessLevelSchema.safeParse(accessLevel).success) {
|
|
601
|
+
throw new Error(
|
|
602
|
+
`invalid access_level "${accessLevel}": expected "read" or "read_write"`
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
423
606
|
var READ_HTTP_METHODS = ["GET", "HEAD", "OPTIONS"];
|
|
424
607
|
var READ_ACTION_TYPES = [
|
|
425
608
|
"read",
|
|
@@ -439,7 +622,19 @@ function isWriteRequest(context) {
|
|
|
439
622
|
}
|
|
440
623
|
function buildConnectionPolicy(options) {
|
|
441
624
|
const { connectionId, selectedApi, accessLevel } = options;
|
|
625
|
+
requireAccessLevel(accessLevel);
|
|
626
|
+
if (!CONNECTION_ID_PATTERN.test(connectionId)) {
|
|
627
|
+
throw new Error(
|
|
628
|
+
`invalid connection_id "${connectionId}": must not contain "/", "*", or whitespace`
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
if (!ACTION_RESOURCE_SEGMENT_PATTERN.test(selectedApi)) {
|
|
632
|
+
throw new Error(
|
|
633
|
+
`invalid selected_api "${selectedApi}": must not contain "/", "*", or whitespace`
|
|
634
|
+
);
|
|
635
|
+
}
|
|
442
636
|
const authStatement = {
|
|
637
|
+
id: `r_${crypto.randomUUID()}`,
|
|
443
638
|
effect: "allow",
|
|
444
639
|
permissions: ["can_use"],
|
|
445
640
|
resources: [`connection/${connectionId}`],
|
|
@@ -459,6 +654,7 @@ function buildConnectionPolicy(options) {
|
|
|
459
654
|
});
|
|
460
655
|
}
|
|
461
656
|
const httpStatement = {
|
|
657
|
+
id: `r_${crypto.randomUUID()}`,
|
|
462
658
|
effect: "allow",
|
|
463
659
|
permissions: ["can_execute"],
|
|
464
660
|
resources: ["http"],
|
|
@@ -467,20 +663,22 @@ function buildConnectionPolicy(options) {
|
|
|
467
663
|
const apiPattern = globSelectedApiVersion(selectedApi);
|
|
468
664
|
const actionResources = accessLevel === "read" ? READ_ACTION_TYPES.map((t) => `action/${apiPattern}/${t}/*`) : [`action/${apiPattern}/*/*`];
|
|
469
665
|
const actionStatement = {
|
|
666
|
+
id: `r_${crypto.randomUUID()}`,
|
|
470
667
|
effect: "allow",
|
|
471
668
|
permissions: ["can_execute"],
|
|
472
669
|
resources: actionResources,
|
|
473
670
|
conditions: [connectionCondition]
|
|
474
671
|
};
|
|
475
672
|
return {
|
|
476
|
-
version:
|
|
477
|
-
|
|
673
|
+
version: 4,
|
|
674
|
+
rules: [[authStatement, httpStatement, actionStatement]]
|
|
478
675
|
};
|
|
479
676
|
}
|
|
480
677
|
|
|
481
678
|
// src/connectionless-policy.ts
|
|
482
679
|
function buildConnectionlessPolicy(options) {
|
|
483
680
|
const { context, accessLevel } = options;
|
|
681
|
+
requireAccessLevel(accessLevel);
|
|
484
682
|
if (context.request_type === "http_request") {
|
|
485
683
|
const conditions = [
|
|
486
684
|
{
|
|
@@ -497,26 +695,35 @@ function buildConnectionlessPolicy(options) {
|
|
|
497
695
|
});
|
|
498
696
|
}
|
|
499
697
|
const statement2 = {
|
|
698
|
+
id: `r_${crypto.randomUUID()}`,
|
|
500
699
|
effect: "allow",
|
|
501
700
|
permissions: ["can_execute"],
|
|
502
701
|
resources: ["http"],
|
|
503
702
|
conditions
|
|
504
703
|
};
|
|
505
|
-
return {
|
|
704
|
+
return {
|
|
705
|
+
version: 4,
|
|
706
|
+
rules: [[statement2]]
|
|
707
|
+
};
|
|
506
708
|
}
|
|
507
709
|
const { selected_api } = parseActionResource(context.resource);
|
|
508
710
|
const apiPattern = globSelectedApiVersion(selected_api);
|
|
509
711
|
const resources = accessLevel === "read" ? READ_ACTION_TYPES.map((t) => `action/${apiPattern}/${t}/*`) : [`action/${apiPattern}/*/*`];
|
|
510
712
|
const statement = {
|
|
713
|
+
id: `r_${crypto.randomUUID()}`,
|
|
511
714
|
effect: "allow",
|
|
512
715
|
permissions: ["can_execute"],
|
|
513
716
|
resources,
|
|
514
717
|
conditions: []
|
|
515
718
|
};
|
|
516
|
-
return {
|
|
719
|
+
return {
|
|
720
|
+
version: 4,
|
|
721
|
+
rules: [[statement]]
|
|
722
|
+
};
|
|
517
723
|
}
|
|
518
724
|
export {
|
|
519
725
|
AccessLevelSchema,
|
|
726
|
+
ActionResourcePatternSchema,
|
|
520
727
|
ActionRunContextInputSchema,
|
|
521
728
|
ActionRunContextSchema,
|
|
522
729
|
ActionTypeSchema,
|
|
@@ -529,6 +736,7 @@ export {
|
|
|
529
736
|
READ_ACTION_TYPES,
|
|
530
737
|
READ_HTTP_METHODS,
|
|
531
738
|
RequestContextSchema,
|
|
739
|
+
ZapierPolicySchema,
|
|
532
740
|
buildActionResource,
|
|
533
741
|
buildActionRunContext,
|
|
534
742
|
buildBroadActionResource,
|
|
@@ -540,6 +748,7 @@ export {
|
|
|
540
748
|
buildHttpRequestContext,
|
|
541
749
|
buildUrlString,
|
|
542
750
|
globSelectedApiVersion,
|
|
751
|
+
isIgnoredHeader,
|
|
543
752
|
isWriteRequest,
|
|
544
753
|
parseActionResource,
|
|
545
754
|
parseUrl,
|