@kensio/yulin 1.20.11 → 1.20.13

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.
Files changed (150) hide show
  1. package/dist/service/acm/command/delete-certificate/delete-certificate-authorizer.js +1 -0
  2. package/dist/service/acm/command/describe-certificate/describe-certificate-authorizer.js +1 -0
  3. package/dist/service/acm/command/list-certificates/list-certificates-authorizer.js +1 -0
  4. package/dist/service/acm/command/request-certificate/request-certificate-authorizer.js +1 -0
  5. package/dist/service/apigateway/serve/auth/sim-rest-api-authorizer-policy.js +3 -0
  6. package/dist/service/apigatewayv2/serve/auth/sim-http-api-authorizer-policy.js +3 -0
  7. package/dist/service/athena/engine/sim-athena-regexp-flags.d.ts +22 -0
  8. package/dist/service/athena/engine/sim-athena-regexp-flags.js +34 -0
  9. package/dist/service/athena/engine/sim-athena-regexp-shims.js +3 -1
  10. package/dist/service/athena/engine/sim-athena-url-parts.d.ts +24 -0
  11. package/dist/service/athena/engine/sim-athena-url-parts.js +76 -0
  12. package/dist/service/athena/engine/sim-athena-url-shims.d.ts +3 -2
  13. package/dist/service/athena/engine/sim-athena-url-shims.js +57 -25
  14. package/dist/service/athena/table/sim-athena-negated-columns.d.ts +17 -0
  15. package/dist/service/athena/table/sim-athena-negated-columns.js +80 -0
  16. package/dist/service/athena/table/sim-athena-partition-filters.d.ts +7 -4
  17. package/dist/service/athena/table/sim-athena-partition-filters.js +12 -5
  18. package/dist/service/aws/factory/sim-aws-account-service-cache.d.ts +3 -0
  19. package/dist/service/aws/factory/sim-aws-account-service-cache.js +5 -0
  20. package/dist/service/aws/factory/sim-aws-service-factory.d.ts +9 -0
  21. package/dist/service/aws/factory/sim-aws-service-factory.js +10 -0
  22. package/dist/service/aws/sim-aws.d.ts +18 -0
  23. package/dist/service/aws/sim-aws.js +19 -0
  24. package/dist/service/cloudformation/resource/cfn/organizations/sim-organizations-cfn-value-adapter.d.ts +6 -0
  25. package/dist/service/cloudformation/resource/cfn/organizations/sim-organizations-cfn-value-adapter.js +23 -0
  26. package/dist/service/cloudformation/resource/cfn/organizations/sim-organizations-cfn.d.ts +66 -0
  27. package/dist/service/cloudformation/resource/cfn/organizations/sim-organizations-cfn.js +136 -0
  28. package/dist/service/cloudformation/resource/cfn/sim-cfn-service-value-adapters.js +2 -0
  29. package/dist/service/cloudformation/resource/resolve/service/sim-cfn-service-factories.js +7 -0
  30. package/dist/service/cloudfront/command/create-distribution/create-distribution-authorizer.js +1 -0
  31. package/dist/service/cloudfront/command/create-function/create-function-authorizer.js +1 -0
  32. package/dist/service/cloudfront/command/delete-distribution/delete-distribution-authorizer.js +1 -0
  33. package/dist/service/cloudfront/command/delete-function/delete-function-authorizer.js +1 -0
  34. package/dist/service/cloudfront/command/get-distribution/get-distribution-authorizer.js +1 -0
  35. package/dist/service/cloudfront/command/update-distribution/update-distribution-authorizer.js +1 -0
  36. package/dist/service/cloudfront/edge/sim-cf-edge-association-auth-z.js +1 -0
  37. package/dist/service/cloudfront/key-value-store/sim-cf-key-value-store-access.js +1 -0
  38. package/dist/service/cloudwatch/command/authorize/sim-cloudwatch-decide.js +1 -0
  39. package/dist/service/cognito/command/authorize/sim-cognito-authorizer.js +1 -0
  40. package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.js +1 -0
  41. package/dist/service/firehose/command/authorize/sim-firehose-authorizer.js +1 -0
  42. package/dist/service/glue/command/authorize/sim-glue-authorize-resources.js +1 -0
  43. package/dist/service/iam/authorize/allow/sim-iam-policy-evaluation.d.ts +45 -0
  44. package/dist/service/iam/authorize/allow/sim-iam-policy-evaluation.js +59 -0
  45. package/dist/service/iam/authorize/context/sim-iam-auth-z-context-builder.d.ts +15 -4
  46. package/dist/service/iam/authorize/context/sim-iam-auth-z-context-builder.js +15 -4
  47. package/dist/service/iam/authorize/context/sim-iam-auth-z-context.d.ts +31 -1
  48. package/dist/service/iam/authorize/context/sim-iam-auth-z-context.factory.js +1 -0
  49. package/dist/service/iam/authorize/context/sim-iam-auth-z-scp-source-builder.d.ts +46 -0
  50. package/dist/service/iam/authorize/context/sim-iam-auth-z-scp-source-builder.js +71 -0
  51. package/dist/service/iam/authorize/scp/sim-iam-scp-gate.d.ts +96 -0
  52. package/dist/service/iam/authorize/scp/sim-iam-scp-gate.js +148 -0
  53. package/dist/service/iam/authorize/scp/sim-iam-scp-resolver.d.ts +53 -0
  54. package/dist/service/iam/authorize/scp/sim-iam-scp-resolver.js +1 -0
  55. package/dist/service/iam/authorize/sim-iam-account-auth-z.d.ts +8 -0
  56. package/dist/service/iam/authorize/sim-iam-account-auth-z.js +1 -0
  57. package/dist/service/iam/authorize/sim-iam-action-authorizer.js +1 -0
  58. package/dist/service/iam/authorize/sim-iam-authorizer.d.ts +2 -0
  59. package/dist/service/iam/authorize/sim-iam-authorizer.js +1 -0
  60. package/dist/service/iam/authorize/sim-iam-decision-value.d.ts +13 -0
  61. package/dist/service/iam/authorize/sim-iam-decision-value.js +12 -0
  62. package/dist/service/iam/authorize/sim-iam-decision.d.ts +33 -16
  63. package/dist/service/iam/authorize/sim-iam-decision.js +64 -49
  64. package/dist/service/iam/authorize/sim-iam-inter-service-auth-z.d.ts +8 -0
  65. package/dist/service/iam/authorize/sim-iam-inter-service-auth-z.js +1 -0
  66. package/dist/service/iam/error/sim-iam.error.d.ts +8 -0
  67. package/dist/service/iam/error/sim-iam.error.js +2 -1
  68. package/dist/service/iam/sim-iam-account-parts.d.ts +9 -0
  69. package/dist/service/iam/sim-iam-account-parts.js +1 -0
  70. package/dist/service/kinesis/command/authorize/sim-kinesis-authorizer.js +1 -0
  71. package/dist/service/kms/command/authorize/sim-kms-authorizer.js +2 -0
  72. package/dist/service/lambda/command/create-function/create-function-authorizer.js +1 -0
  73. package/dist/service/lambda/command/delete-function/delete-function-authorizer.js +1 -0
  74. package/dist/service/lambda/command/event-invoke-config/event-invoke-config-authorizer.js +1 -0
  75. package/dist/service/lambda/command/function-url/function-url-authorizer.js +1 -0
  76. package/dist/service/lambda/command/get-function/get-function-authorizer.js +1 -0
  77. package/dist/service/lambda/command/invoke/invoke-authorizer.js +1 -0
  78. package/dist/service/lambda/command/list-functions/list-functions-authorizer.js +1 -0
  79. package/dist/service/lambda/command/update-function-code/update-function-code-authorizer.js +1 -0
  80. package/dist/service/lambda/command/update-function-configuration/update-function-configuration-authorizer.js +1 -0
  81. package/dist/service/lambda/command/version/version-authorizer.js +1 -0
  82. package/dist/service/logs/command/authorize/sim-logs-authorizer.js +1 -0
  83. package/dist/service/organizations/cfn/sim-cfn-organization-creator.d.ts +19 -0
  84. package/dist/service/organizations/cfn/sim-cfn-organization-creator.js +25 -0
  85. package/dist/service/organizations/cfn/sim-cfn-organizational-unit-creator.d.ts +16 -0
  86. package/dist/service/organizations/cfn/sim-cfn-organizational-unit-creator.js +21 -0
  87. package/dist/service/organizations/cfn/sim-cfn-organizations-account-creator.d.ts +20 -0
  88. package/dist/service/organizations/cfn/sim-cfn-organizations-account-creator.js +34 -0
  89. package/dist/service/organizations/cfn/sim-cfn-organizations-policy-creator.d.ts +21 -0
  90. package/dist/service/organizations/cfn/sim-cfn-organizations-policy-creator.js +40 -0
  91. package/dist/service/organizations/cfn/sim-cfn-organizations-policy-input.d.ts +20 -0
  92. package/dist/service/organizations/cfn/sim-cfn-organizations-policy-input.js +33 -0
  93. package/dist/service/organizations/cfn/sim-cfn-organizations-properties.d.ts +36 -0
  94. package/dist/service/organizations/cfn/sim-cfn-organizations-properties.js +78 -0
  95. package/dist/service/organizations/cfn/sim-cfn-organizations-record.d.ts +40 -0
  96. package/dist/service/organizations/cfn/sim-cfn-organizations-record.js +54 -0
  97. package/dist/service/organizations/cfn/sim-cfn-organizations-remover.d.ts +17 -0
  98. package/dist/service/organizations/cfn/sim-cfn-organizations-remover.js +40 -0
  99. package/dist/service/organizations/cfn/sim-organizations-cfn-resource-factory.d.ts +24 -0
  100. package/dist/service/organizations/cfn/sim-organizations-cfn-resource-factory.js +50 -0
  101. package/dist/service/organizations/index.d.ts +6 -0
  102. package/dist/service/organizations/index.js +6 -0
  103. package/dist/service/organizations/policy/sim-organizations-effective-policies.d.ts +43 -0
  104. package/dist/service/organizations/policy/sim-organizations-effective-policies.js +58 -0
  105. package/dist/service/organizations/policy/sim-organizations-scp-store.d.ts +61 -0
  106. package/dist/service/organizations/policy/sim-organizations-scp-store.js +103 -0
  107. package/dist/service/organizations/sim-organizations-structure.d.ts +94 -0
  108. package/dist/service/organizations/sim-organizations-structure.js +140 -0
  109. package/dist/service/organizations/sim-organizations.d.ts +95 -0
  110. package/dist/service/organizations/sim-organizations.js +99 -0
  111. package/dist/service/organizations/tree/sim-organizations-node.d.ts +57 -0
  112. package/dist/service/organizations/tree/sim-organizations-node.js +58 -0
  113. package/dist/service/organizations/tree/sim-organizations-tree.d.ts +91 -0
  114. package/dist/service/organizations/tree/sim-organizations-tree.js +154 -0
  115. package/dist/service/route53/command/change-resource-record-sets/change-resource-record-sets-authorizer.js +1 -0
  116. package/dist/service/route53/command/create-hosted-zone/create-hosted-zone-authorizer.js +1 -0
  117. package/dist/service/route53/command/delete-hosted-zone/delete-hosted-zone-authorizer.js +1 -0
  118. package/dist/service/route53/command/dnssec/sim-route53-dnssec-authorizer.js +1 -0
  119. package/dist/service/route53/command/get-hosted-zone/get-hosted-zone-authorizer.js +1 -0
  120. package/dist/service/route53/command/list-hosted-zones-by-name/list-hosted-zones-by-name-authorizer.js +1 -0
  121. package/dist/service/route53/command/list-resource-record-sets/list-resource-record-sets-authorizer.js +1 -0
  122. package/dist/service/s3/command/create-bucket/create-bucket.handler.js +1 -0
  123. package/dist/service/s3/command/delete-bucket/delete-bucket-authorizer.js +1 -0
  124. package/dist/service/s3/command/delete-bucket-policy/delete-bucket-policy-authorizer.js +1 -0
  125. package/dist/service/s3/command/delete-object/delete-object-authorizer.js +1 -0
  126. package/dist/service/s3/command/get-bucket-policy/get-bucket-policy-authorizer.js +1 -0
  127. package/dist/service/s3/command/get-object/get-object-authorizer.js +1 -0
  128. package/dist/service/s3/command/head-bucket/head-bucket-authorizer.js +1 -0
  129. package/dist/service/s3/command/lifecycle/sim-s3-lifecycle-authorizer.js +1 -0
  130. package/dist/service/s3/command/list-buckets/list-buckets-authorizer.js +1 -0
  131. package/dist/service/s3/command/list-objects/list-objects-authorizer.js +1 -0
  132. package/dist/service/s3/command/multipart/sim-s3-multipart-authorizer.js +1 -0
  133. package/dist/service/s3/command/notification/sim-s3-notification-authorizer.js +1 -0
  134. package/dist/service/s3/command/public-access-block/sim-s3-public-access-block-authorizer.js +1 -0
  135. package/dist/service/s3/command/put-bucket-policy/put-bucket-policy-authorizer.js +1 -0
  136. package/dist/service/s3/command/put-bucket-website/put-bucket-website-authorizer.js +1 -0
  137. package/dist/service/s3/command/put-object/put-object-authorizer.js +1 -0
  138. package/dist/service/secretsmanager/command/authorize/sim-secrets-manager-authorizer.js +1 -0
  139. package/dist/service/ses/command/authorize/sim-ses-authorizer.js +1 -0
  140. package/dist/service/sqs/command/authorize/sim-sqs-authorizer.js +1 -0
  141. package/dist/service/ssm/command/authorize/sim-ssm-authorizer.js +1 -0
  142. package/dist/service/wafv2/command/authorize/sim-wafv2-authorizer.js +1 -0
  143. package/dist/service/wafv2/command/ip-set/sim-wafv2-ip-set-commands.js +4 -3
  144. package/dist/service/wafv2/command/regex-pattern-set/sim-wafv2-regex-pattern-set-commands.js +4 -3
  145. package/dist/service/wafv2/command/sim-wafv2-input.d.ts +15 -0
  146. package/dist/service/wafv2/command/sim-wafv2-input.js +66 -1
  147. package/dist/service/wafv2/command/web-acl/sim-wafv2-web-acl-commands.js +7 -5
  148. package/dist/service/wafv2/error/sim-wafv2.error.d.ts +11 -0
  149. package/dist/service/wafv2/error/sim-wafv2.error.js +13 -0
  150. package/package.json +5 -1
@@ -27,6 +27,7 @@ export class DeleteCertificateAuthorizer {
27
27
  if (decision.isDenied) {
28
28
  throw new SimIamAccessDenied({
29
29
  principal: decision.caller.principal,
30
+ reason: decision.denialReason,
30
31
  action: DeleteCertificateAuthorizer.action,
31
32
  resource: certificateArn,
32
33
  });
@@ -32,6 +32,7 @@ export class DescribeCertificateAuthorizer {
32
32
  if (decision.isDenied) {
33
33
  throw new SimIamAccessDenied({
34
34
  principal: decision.caller.principal,
35
+ reason: decision.denialReason,
35
36
  action: DescribeCertificateAuthorizer.action,
36
37
  resource: certificateArn,
37
38
  });
@@ -32,6 +32,7 @@ export class ListCertificatesAuthorizer {
32
32
  if (decision.isDenied) {
33
33
  throw new SimIamAccessDenied({
34
34
  principal: decision.caller.principal,
35
+ reason: decision.denialReason,
35
36
  action: ListCertificatesAuthorizer.action,
36
37
  resource: ListCertificatesAuthorizer.resource,
37
38
  });
@@ -29,6 +29,7 @@ export class RequestCertificateAuthorizer {
29
29
  if (decision.isDenied) {
30
30
  throw new SimIamAccessDenied({
31
31
  principal: decision.caller.principal,
32
+ reason: decision.denialReason,
32
33
  action: RequestCertificateAuthorizer.action,
33
34
  resource: this.resource,
34
35
  });
@@ -49,6 +49,9 @@ export class SimRestApiAuthorizerPolicy {
49
49
  },
50
50
  ],
51
51
  resourcePolicies: [],
52
+ // An authorizer's policy document is evaluated on its own. No Account
53
+ // boundary is being crossed here, so no service control policy applies.
54
+ serviceControlPolicies: { applies: false, levels: [] },
52
55
  allowRequirement: new SimIamEitherSideAllowRequirement(),
53
56
  action: simExecuteApiInvokeAction,
54
57
  resource: methodArn,
@@ -39,6 +39,9 @@ export class SimHttpApiAuthorizerPolicy {
39
39
  },
40
40
  ],
41
41
  resourcePolicies: [],
42
+ // An authorizer's policy document is evaluated on its own. No Account
43
+ // boundary is being crossed here, so no service control policy applies.
44
+ serviceControlPolicies: { applies: false, levels: [] },
42
45
  allowRequirement: new SimIamEitherSideAllowRequirement(),
43
46
  action: simExecuteApiInvokeAction,
44
47
  resource: routeArn,
@@ -0,0 +1,22 @@
1
+ /** One pattern and the flags to compile it with. */
2
+ export interface SimAthenaLiftedPattern {
3
+ readonly pattern: string;
4
+ readonly flags: string;
5
+ }
6
+ /**
7
+ * One pattern with the flags Java writes inside it lifted out beside it.
8
+ *
9
+ * Athena's regular expressions are Joni, which reads `(?i)` at the head of a
10
+ * pattern as a flag over the whole of it. JavaScript reads the same text as a
11
+ * group and refuses it. `(?i)`, `(?m)` and `(?s)` map onto flags of the same
12
+ * letter, and lifting them is all it takes.
13
+ *
14
+ * A flag Joni has and JavaScript has not, `(?x)` among them, stays in the
15
+ * pattern for `RegExp` to turn down. So does a group written anywhere but the
16
+ * head, since JavaScript has no way to turn a flag on part way through. The
17
+ * scoped form `(?i:...)` needs nothing done to it and already runs.
18
+ *
19
+ * A flag group captures nothing in Java, so taking one off the front leaves
20
+ * the capture groups numbered as the statement wrote them.
21
+ */
22
+ export declare function simAthenaLiftedPattern(pattern: string, flags: string): SimAthenaLiftedPattern;
@@ -0,0 +1,34 @@
1
+ /** One of Java's inline flag groups at the head of a pattern, as `(?i)`. */
2
+ const leadingFlag = /^\(\?[ims]+\)/u;
3
+ /**
4
+ * One pattern with the flags Java writes inside it lifted out beside it.
5
+ *
6
+ * Athena's regular expressions are Joni, which reads `(?i)` at the head of a
7
+ * pattern as a flag over the whole of it. JavaScript reads the same text as a
8
+ * group and refuses it. `(?i)`, `(?m)` and `(?s)` map onto flags of the same
9
+ * letter, and lifting them is all it takes.
10
+ *
11
+ * A flag Joni has and JavaScript has not, `(?x)` among them, stays in the
12
+ * pattern for `RegExp` to turn down. So does a group written anywhere but the
13
+ * head, since JavaScript has no way to turn a flag on part way through. The
14
+ * scoped form `(?i:...)` needs nothing done to it and already runs.
15
+ *
16
+ * A flag group captures nothing in Java, so taking one off the front leaves
17
+ * the capture groups numbered as the statement wrote them.
18
+ */
19
+ export function simAthenaLiftedPattern(pattern, flags) {
20
+ const letters = new Set();
21
+ let rest = pattern;
22
+ let head = leadingFlag.exec(rest);
23
+ while (head !== null) {
24
+ // Everything the group holds between its `(?` and its `)`.
25
+ const written = head[0].slice(2, -1);
26
+ for (const letter of written) {
27
+ letters.add(letter);
28
+ }
29
+ rest = rest.slice(head[0].length);
30
+ head = leadingFlag.exec(rest);
31
+ }
32
+ const added = [...letters].filter((letter) => !flags.includes(letter));
33
+ return { pattern: rest, flags: flags + added.join("") };
34
+ }
@@ -1,4 +1,5 @@
1
1
  import { SimAthenaSetUpError } from "../error/sim-athena.error.js";
2
+ import { simAthenaLiftedPattern } from "./sim-athena-regexp-flags.js";
2
3
  import { isExplicitNull, shimNumber, shimText, simAthenaScalarShim, } from "./sim-athena-shim-registry.js";
3
4
  /**
4
5
  * Trino's regular expression functions.
@@ -79,10 +80,11 @@ function expressionFor(pattern, flags = "u") {
79
80
  if (pattern === undefined) {
80
81
  return undefined;
81
82
  }
83
+ const lifted = simAthenaLiftedPattern(pattern, flags);
82
84
  try {
83
85
  // The pattern is the query's own, which is the whole point of the function.
84
86
  // oxlint-disable-next-line security/detect-non-literal-regexp
85
- return new RegExp(pattern, flags);
87
+ return new RegExp(lifted.pattern, lifted.flags);
86
88
  }
87
89
  catch {
88
90
  return undefined;
@@ -0,0 +1,24 @@
1
+ /** One URI reference split the way Java's `URI` splits it. */
2
+ export interface SimAthenaUrlParts {
3
+ readonly protocol: string;
4
+ readonly host: string;
5
+ readonly port: number | null;
6
+ readonly path: string;
7
+ readonly query: string;
8
+ readonly fragment: string;
9
+ }
10
+ /**
11
+ * One URI reference split into its parts, or nothing where it is no reference.
12
+ *
13
+ * Trino reads a URL with `new URI(text)`, which takes a reference with no
14
+ * scheme of its own. `/reports/august?tenant=acme` is one, and it is the shape
15
+ * a CloudFront access log holds, since the log carries the path and the query
16
+ * in columns of their own and no whole URL anywhere.
17
+ *
18
+ * The parts are read off the text rather than resolved against a base. That is
19
+ * what `URI` does, and it keeps a relative path as the text wrote it.
20
+ *
21
+ * A part the reference leaves out comes back as the empty string, which is
22
+ * what Trino answers for one. The port has no empty form and answers null.
23
+ */
24
+ export declare function simAthenaUrlParts(text: string): SimAthenaUrlParts | undefined;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * A URI reference, split as RFC 3986 writes the expression for it.
3
+ *
4
+ * This is the expression the RFC prints in its own appendix, character for
5
+ * character. Its groups are optional and never nested, so what a reference
6
+ * that does not match costs in backtracking is bounded by the text's length.
7
+ */
8
+ const reference =
9
+ // oxlint-disable-next-line security/detect-unsafe-regex
10
+ /^(?:([^:/?#]+):)?(?:\/\/([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?$/u;
11
+ /** The characters RFC 2396 allows, which is what Java's `URI` takes. */
12
+ const disallowed = /[^A-Za-z0-9;/?:@&=+$,\-_.!~*'()%[\]#]/u;
13
+ /** A percent naming no byte, which Java's `URI` reads as malformed. */
14
+ const malformedEscape = /%(?![0-9A-Fa-f]{2})/u;
15
+ /** A scheme starts with a letter and carries no other punctuation. */
16
+ const schemeName = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
17
+ const digits = /^\d+$/u;
18
+ /**
19
+ * One URI reference split into its parts, or nothing where it is no reference.
20
+ *
21
+ * Trino reads a URL with `new URI(text)`, which takes a reference with no
22
+ * scheme of its own. `/reports/august?tenant=acme` is one, and it is the shape
23
+ * a CloudFront access log holds, since the log carries the path and the query
24
+ * in columns of their own and no whole URL anywhere.
25
+ *
26
+ * The parts are read off the text rather than resolved against a base. That is
27
+ * what `URI` does, and it keeps a relative path as the text wrote it.
28
+ *
29
+ * A part the reference leaves out comes back as the empty string, which is
30
+ * what Trino answers for one. The port has no empty form and answers null.
31
+ */
32
+ export function simAthenaUrlParts(text) {
33
+ if (disallowed.test(text) || malformedEscape.test(text)) {
34
+ return undefined;
35
+ }
36
+ const found = reference.exec(text);
37
+ if (found === null) {
38
+ return undefined;
39
+ }
40
+ const scheme = found.at(1);
41
+ if (scheme !== undefined && !schemeName.test(scheme)) {
42
+ return undefined;
43
+ }
44
+ const authority = found.at(2);
45
+ const path = found.at(3) ?? "";
46
+ // A scheme followed by anything but a slash is an opaque URI, and Java reads
47
+ // neither a path nor a query out of one. `mailto:a@b` is the case.
48
+ const opaque = scheme !== undefined && authority === undefined && !path.startsWith("/");
49
+ return {
50
+ protocol: scheme ?? "",
51
+ ...hostAndPort(authority),
52
+ path: opaque ? "" : path,
53
+ query: opaque ? "" : (found.at(4) ?? ""),
54
+ fragment: found.at(5) ?? "",
55
+ };
56
+ }
57
+ /**
58
+ * The host and the port one authority names.
59
+ *
60
+ * The user's own credentials sit before an `@` and are no part of the host, so
61
+ * the digits in `user:1234@rain.example` are no port. A colon with anything
62
+ * but digits after it belongs to the host, which is what keeps the one inside
63
+ * an IPv6 literal out of the port.
64
+ */
65
+ function hostAndPort(authority) {
66
+ if (authority === undefined) {
67
+ return { host: "", port: null };
68
+ }
69
+ const written = authority.slice(authority.lastIndexOf("@") + 1);
70
+ const colon = written.lastIndexOf(":");
71
+ const port = written.slice(colon + 1);
72
+ if (colon === -1 || !digits.test(port)) {
73
+ return { host: written, port: null };
74
+ }
75
+ return { host: written.slice(0, colon), port: Number(port) };
76
+ }
@@ -2,8 +2,9 @@ import type { DatabaseSync } from "node:sqlite";
2
2
  /**
3
3
  * Trino's URL functions, which a query over access logs reaches for.
4
4
  *
5
- * Trino fails a query over text that is no URL and these answer null, the same
6
- * forgiving direction the rest of the engine takes.
5
+ * The extract functions answer null over text that is no URI reference, and so
6
+ * does Trino. Each one is `neverFails` there and answers null off a `URI` that
7
+ * would not parse.
7
8
  *
8
9
  * Trino answers with an empty string for a part the URL leaves out, apart from
9
10
  * the port, which has no empty form and answers null.
@@ -1,17 +1,20 @@
1
1
  import { shimText, simAthenaScalarShim } from "./sim-athena-shim-registry.js";
2
- /** What each `url_extract` function reads off a parsed URL. */
2
+ import { simAthenaUrlParts, } from "./sim-athena-url-parts.js";
3
+ /** What each `url_extract` function reads off a split reference. */
3
4
  const parts = new Map([
4
- ["url_extract_host", (url) => url.hostname],
5
- ["url_extract_path", (url) => url.pathname],
6
- ["url_extract_protocol", (url) => url.protocol.replace(":", "")],
7
- ["url_extract_fragment", (url) => url.hash.replace("#", "")],
8
- ["url_extract_query", (url) => url.search.replace("?", "")],
5
+ ["url_extract_host", (url) => url.host],
6
+ ["url_extract_path", (url) => url.path],
7
+ ["url_extract_protocol", (url) => url.protocol],
8
+ ["url_extract_fragment", (url) => url.fragment],
9
+ ["url_extract_query", (url) => url.query],
10
+ ["url_extract_port", (url) => url.port],
9
11
  ]);
10
12
  /**
11
13
  * Trino's URL functions, which a query over access logs reaches for.
12
14
  *
13
- * Trino fails a query over text that is no URL and these answer null, the same
14
- * forgiving direction the rest of the engine takes.
15
+ * The extract functions answer null over text that is no URI reference, and so
16
+ * does Trino. Each one is `neverFails` there and answers null off a `URI` that
17
+ * would not parse.
15
18
  *
16
19
  * Trino answers with an empty string for a part the URL leaves out, apart from
17
20
  * the port, which has no empty form and answers null.
@@ -19,39 +22,68 @@ const parts = new Map([
19
22
  export function simAthenaInstallUrlShims(database) {
20
23
  for (const [name, read] of parts) {
21
24
  simAthenaScalarShim(database, name, (value) => {
22
- const url = parsedUrl(shimText(value));
25
+ const url = readParts(shimText(value));
23
26
  return url === undefined ? null : read(url);
24
27
  });
25
28
  }
26
- simAthenaScalarShim(database, "url_extract_port", (value) => writtenPort(shimText(value)));
27
29
  simAthenaScalarShim(database, "url_extract_parameter", (value, name) => {
28
30
  const wanted = shimText(name);
29
- const url = parsedUrl(shimText(value));
31
+ const url = readParts(shimText(value));
30
32
  if (url === undefined || wanted === undefined) {
31
33
  return null;
32
34
  }
33
- return url.searchParams.get(wanted);
35
+ return new URLSearchParams(url.query).get(wanted);
34
36
  });
37
+ simAthenaScalarShim(database, "url_decode", (value) => decoded(shimText(value)));
38
+ simAthenaScalarShim(database, "url_encode", (value) => encoded(shimText(value)));
35
39
  }
36
- /** Everything between the scheme and the path, which is where a port is written. */
37
- const authority = /^[A-Za-z][\w+.-]*:\/\/[^/?#]*/u;
38
40
  /**
39
- * The port one URL names, or nothing where it names none.
41
+ * One value with its escapes read back, the way `url_decode` reads them.
40
42
  *
41
- * Read off the text rather than off the parsed URL, because the parser drops a
42
- * port that is the scheme's own default. Trino answers with the port a URL was
43
- * written with, so `http://rain.example:80/a` is eighty rather than nothing.
43
+ * Trino runs `URLDecoder.decode` over UTF-8, which reads `+` as a space and
44
+ * `%2B` as a plus. Replacing the plus before decoding is what keeps the two
45
+ * apart.
46
+ *
47
+ * Trino raises over an escape that names no byte, and writes a replacement
48
+ * character where the bytes it names are no UTF-8. `decodeURIComponent` throws
49
+ * over both, and this answers null for both, the same forgiving direction the
50
+ * rest of the file takes.
44
51
  */
45
- function writtenPort(value) {
46
- if (value === undefined || parsedUrl(value) === undefined) {
52
+ function decoded(value) {
53
+ if (value === undefined) {
54
+ return null;
55
+ }
56
+ try {
57
+ return decodeURIComponent(value.replaceAll("+", " "));
58
+ }
59
+ catch {
47
60
  return null;
48
61
  }
49
- const written = /:(\d+)$/u.exec(authority.exec(value)?.[0] ?? "")?.[1];
50
- return written === undefined ? null : Number(written);
51
62
  }
52
- function parsedUrl(value) {
63
+ /**
64
+ * One value escaped for a query string, the way `url_encode` escapes it.
65
+ *
66
+ * Trino runs Guava's form-parameter escaper, which keeps `-`, `_`, `.` and `*`
67
+ * alone and writes a space as `+`. `encodeURIComponent` keeps five characters
68
+ * beyond those four, and each of those is escaped here.
69
+ *
70
+ * No escape written here carries a character a later pass looks for, and a
71
+ * space is the only character `encodeURIComponent` writes as `%20`, since a
72
+ * percent in the value has already become `%25` by then.
73
+ */
74
+ function encoded(value) {
53
75
  if (value === undefined) {
54
- return undefined;
76
+ return null;
55
77
  }
56
- return URL.parse(value) ?? undefined;
78
+ return encodeURIComponent(value)
79
+ .replaceAll("!", "%21")
80
+ .replaceAll("'", "%27")
81
+ .replaceAll("(", "%28")
82
+ .replaceAll(")", "%29")
83
+ .replaceAll("~", "%7E")
84
+ .replaceAll("%20", "+");
85
+ }
86
+ /** One value split into its URL parts, or nothing where it is null or no URL. */
87
+ function readParts(value) {
88
+ return value === undefined ? undefined : simAthenaUrlParts(value);
57
89
  }
@@ -0,0 +1,17 @@
1
+ import type { SimAthenaSqlToken } from "./sim-athena-sql-tokens.js";
2
+ /**
3
+ * The columns a `NOT` in this query could have negated.
4
+ *
5
+ * A negated value names the partition a query does not want, so a column under
6
+ * a `NOT` is left unconstrained. A query filtering bots out of a day's logs
7
+ * negates nothing about the day, and reading how far each `NOT` reaches is
8
+ * what tells the two apart.
9
+ *
10
+ * An infix `NOT`, as in `day NOT IN ('a')`, is read as reaching nothing. Its
11
+ * own column keeps whatever else the statement constrains it to, and that is
12
+ * always a superset of what Athena reads, since `day = 'a' AND day NOT IN (x)`
13
+ * can only ever narrow. The query applies the exclusion itself once the rows
14
+ * are in. Naming the column here would drop its filter and widen the scan to
15
+ * every partition.
16
+ */
17
+ export declare function simAthenaNegatedColumns(tokens: readonly SimAthenaSqlToken[]): ReadonlySet<string>;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The columns a `NOT` in this query could have negated.
3
+ *
4
+ * A negated value names the partition a query does not want, so a column under
5
+ * a `NOT` is left unconstrained. A query filtering bots out of a day's logs
6
+ * negates nothing about the day, and reading how far each `NOT` reaches is
7
+ * what tells the two apart.
8
+ *
9
+ * An infix `NOT`, as in `day NOT IN ('a')`, is read as reaching nothing. Its
10
+ * own column keeps whatever else the statement constrains it to, and that is
11
+ * always a superset of what Athena reads, since `day = 'a' AND day NOT IN (x)`
12
+ * can only ever narrow. The query applies the exclusion itself once the rows
13
+ * are in. Naming the column here would drop its filter and widen the scan to
14
+ * every partition.
15
+ */
16
+ export function simAthenaNegatedColumns(tokens) {
17
+ const negated = new Set();
18
+ for (const [position, token] of tokens.entries()) {
19
+ if (token.kind === "word" && token.text === "not") {
20
+ for (const name of namesUnder(tokens, position)) {
21
+ negated.add(name.toLowerCase());
22
+ }
23
+ }
24
+ }
25
+ return negated;
26
+ }
27
+ /** The words a `NOT` reaches no further than, `OR` aside. */
28
+ const boundary = new Set([
29
+ "and",
30
+ "except",
31
+ "from",
32
+ "group",
33
+ "having",
34
+ "intersect",
35
+ "limit",
36
+ "offset",
37
+ "order",
38
+ "union",
39
+ "where",
40
+ "window",
41
+ ]);
42
+ /**
43
+ * The identifiers one `NOT` reaches.
44
+ *
45
+ * `NOT` binds tighter than `AND`, so its reach ends at the next of the words
46
+ * above, outside any brackets, and at the bracket the `NOT` itself sits in.
47
+ * `OR` is absent from that list because a query carrying one is left
48
+ * unfiltered before this runs.
49
+ *
50
+ * Nothing here parses, so a name a `NOT` could not really have reached is
51
+ * dropped anyway. That direction only ever leaves a column unfiltered, which
52
+ * is the answer a query with no filter gets.
53
+ */
54
+ function namesUnder(tokens, position) {
55
+ const names = [];
56
+ let depth = 0;
57
+ let cursor = position + 1;
58
+ while (cursor < tokens.length) {
59
+ const token = tokens.at(cursor);
60
+ if (token?.kind === "symbol" && token.text === "(") {
61
+ depth += 1;
62
+ }
63
+ else if (token?.kind === "symbol" && token.text === ")") {
64
+ if (depth === 0) {
65
+ return names;
66
+ }
67
+ depth -= 1;
68
+ }
69
+ else if (depth === 0 &&
70
+ token?.kind === "word" &&
71
+ boundary.has(token.text)) {
72
+ return names;
73
+ }
74
+ else if (token?.kind === "word" || token?.kind === "quoted") {
75
+ names.push(token.text);
76
+ }
77
+ cursor += 1;
78
+ }
79
+ return names;
80
+ }
@@ -2,10 +2,13 @@
2
2
  * The partition values a query's `WHERE` clause pins down.
3
3
  *
4
4
  * Only two shapes are read, `column = 'value'` and `column IN ('a', 'b')`, and
5
- * they are read wherever they appear. A query carrying `OR` or `NOT` anywhere
6
- * is left unfiltered. A value under one arm of an `OR` constrains nothing on
7
- * its own, and a negated one names the partition the query does not want, so
8
- * reading either as a constraint would answer from the wrong prefixes.
5
+ * they are read wherever they appear. A query carrying `OR` anywhere is left
6
+ * unfiltered, since a value under one arm constrains nothing on its own.
7
+ *
8
+ * A `NOT` leaves the columns it reaches unconstrained and the rest alone. A
9
+ * negated value names the partition the query does not want, and reading it as
10
+ * a constraint would answer from the wrong prefixes. A negation on any other
11
+ * column leaves the partition constraints as true as they were.
9
12
  *
10
13
  * Two constraints on one column are intersected, since a query carrying both
11
14
  * wants the rows they agree on.
@@ -1,12 +1,16 @@
1
+ import { simAthenaNegatedColumns } from "./sim-athena-negated-columns.js";
1
2
  import { simAthenaSqlTokens } from "./sim-athena-sql-tokens.js";
2
3
  /**
3
4
  * The partition values a query's `WHERE` clause pins down.
4
5
  *
5
6
  * Only two shapes are read, `column = 'value'` and `column IN ('a', 'b')`, and
6
- * they are read wherever they appear. A query carrying `OR` or `NOT` anywhere
7
- * is left unfiltered. A value under one arm of an `OR` constrains nothing on
8
- * its own, and a negated one names the partition the query does not want, so
9
- * reading either as a constraint would answer from the wrong prefixes.
7
+ * they are read wherever they appear. A query carrying `OR` anywhere is left
8
+ * unfiltered, since a value under one arm constrains nothing on its own.
9
+ *
10
+ * A `NOT` leaves the columns it reaches unconstrained and the rest alone. A
11
+ * negated value names the partition the query does not want, and reading it as
12
+ * a constraint would answer from the wrong prefixes. A negation on any other
13
+ * column leaves the partition constraints as true as they were.
10
14
  *
11
15
  * Two constraints on one column are intersected, since a query carrying both
12
16
  * wants the rows they agree on.
@@ -52,6 +56,9 @@ export function simAthenaPartitionFilters(sql) {
52
56
  }
53
57
  }
54
58
  }
59
+ for (const name of simAthenaNegatedColumns(tokens)) {
60
+ byColumn.delete(name);
61
+ }
55
62
  return new SimAthenaPartitionFilters(byColumn);
56
63
  }
57
64
  /**
@@ -68,7 +75,7 @@ function narrow(byColumn, columnName, values) {
68
75
  byColumn.set(name, already.filter((value) => wanted.has(value)));
69
76
  }
70
77
  function isUnreadableTerm(token) {
71
- return token.kind === "word" && (token.text === "or" || token.text === "not");
78
+ return token.kind === "word" && token.text === "or";
72
79
  }
73
80
  /**
74
81
  * Read `('a', 'b')` starting at its opening bracket.
@@ -9,6 +9,7 @@ import type { SimRoute53Registry } from "../../route53/registry/sim-route53-regi
9
9
  import type { SimKmsRegistry } from "../../kms/registry/sim-kms-registry.js";
10
10
  import { SimIam } from "../../iam/index.js";
11
11
  import type { SimIamRegistry } from "../../iam/registry/sim-iam-registry.js";
12
+ import type { SimIamServiceControlPolicyResolver } from "../../iam/authorize/scp/sim-iam-scp-resolver.js";
12
13
  import { type SimIamAccessKeyRegistry } from "../../iam/registry/sim-iam-access-key-registry.js";
13
14
  import type { SimAwsServiceHosts } from "../../../serve/controller/host/sim-aws-service-hosts.js";
14
15
  import type { SimAwsScopedServiceProperties } from "./sim-aws-scoped-service-properties.js";
@@ -23,6 +24,7 @@ interface SimAwsAccountServiceCacheProperties {
23
24
  readonly shadowableServiceHosts: SimAwsServiceHosts;
24
25
  readonly iamRegistry: SimIamRegistry;
25
26
  readonly accessKeyRegistry: SimIamAccessKeyRegistry;
27
+ readonly scpResolver: SimIamServiceControlPolicyResolver;
26
28
  }
27
29
  /**
28
30
  * Account-level cache for simulated AWS service instances.
@@ -52,6 +54,7 @@ export declare class SimAwsAccountServiceCache {
52
54
  private readonly cloudFrontRegistry;
53
55
  private readonly iamRegistry;
54
56
  private readonly accessKeyRegistry;
57
+ private readonly scpResolver;
55
58
  private readonly route53Registry;
56
59
  private readonly kmsRegistry;
57
60
  private readonly serviceHosts;
@@ -37,6 +37,7 @@ export class SimAwsAccountServiceCache {
37
37
  cloudFrontRegistry;
38
38
  iamRegistry;
39
39
  accessKeyRegistry;
40
+ scpResolver;
40
41
  route53Registry;
41
42
  kmsRegistry;
42
43
  serviceHosts;
@@ -55,6 +56,7 @@ export class SimAwsAccountServiceCache {
55
56
  this.acmRegistry = properties.acmRegistry;
56
57
  this.cloudFrontRegistry = properties.cloudFrontRegistry;
57
58
  this.iamRegistry = properties.iamRegistry;
59
+ this.scpResolver = properties.scpResolver;
58
60
  this.accessKeyRegistry = properties.accessKeyRegistry;
59
61
  this.route53Registry = properties.route53Registry;
60
62
  this.kmsRegistry = properties.kmsRegistry;
@@ -109,6 +111,9 @@ export class SimAwsAccountServiceCache {
109
111
  // A cross-Account request is decided in both Accounts, so this IAM
110
112
  // needs to be able to reach the IAM of the caller's own Account.
111
113
  iamResolver: this.iamRegistry,
114
+ // What the Account's organization allows it to do is decided outside
115
+ // the Account, so IAM asks simulated Organizations about it.
116
+ scpResolver: this.scpResolver,
112
117
  // Access keys are indexed simulation-wide as they are issued, so a
113
118
  // signed request naming only a key id can be traced to this Account.
114
119
  credentialRegistry: new SimIamCredentialRegistry({
@@ -22,6 +22,7 @@ import type { SimScheduler } from "../../scheduler/index.js";
22
22
  import type { SimElbV2 } from "../../elbv2/index.js";
23
23
  import type { SimIam } from "../../iam/index.js";
24
24
  import { SimIamRegistry } from "../../iam/registry/sim-iam-registry.js";
25
+ import { SimOrganizations } from "../../organizations/index.js";
25
26
  import type { SimFirehose } from "../../firehose/index.js";
26
27
  import type { SimGlue } from "../../glue/index.js";
27
28
  import type { SimKinesis } from "../../kinesis/index.js";
@@ -77,6 +78,14 @@ export declare class SimAwsServiceFactory {
77
78
  * instance so cross-account services can resolve Account-owned IAM state.
78
79
  */
79
80
  readonly iamRegistry: SimIamRegistry;
81
+ /**
82
+ * Simulated AWS Organizations for this simulation.
83
+ *
84
+ * An organization spans Accounts, so one belongs to the simulation rather
85
+ * than to any scope in it. Sim IAM reads the service control policies it
86
+ * holds when it decides a request.
87
+ */
88
+ readonly organizations: SimOrganizations;
80
89
  /**
81
90
  * Shared request authentication collaborators for this simulation.
82
91
  * @internal
@@ -1,5 +1,6 @@
1
1
  import { SimCloudFrontRegistry } from "../../cloudfront/registry/sim-cloud-front-registry.js";
2
2
  import { SimIamRegistry } from "../../iam/registry/sim-iam-registry.js";
3
+ import { SimOrganizations } from "../../organizations/index.js";
3
4
  import { SimLambdaUrlRegistry } from "../../lambda/registry/sim-lambda-url-registry.js";
4
5
  import { SimAwsAccountRegionServiceBuilder } from "./sim-aws-account-region-service-builder.js";
5
6
  import { SimAwsRegisteredServiceBuilder } from "./sim-aws-registered-service-builder.js";
@@ -40,6 +41,14 @@ export class SimAwsServiceFactory {
40
41
  * instance so cross-account services can resolve Account-owned IAM state.
41
42
  */
42
43
  iamRegistry = new SimIamRegistry();
44
+ /**
45
+ * Simulated AWS Organizations for this simulation.
46
+ *
47
+ * An organization spans Accounts, so one belongs to the simulation rather
48
+ * than to any scope in it. Sim IAM reads the service control policies it
49
+ * holds when it decides a request.
50
+ */
51
+ organizations = new SimOrganizations();
43
52
  /**
44
53
  * Shared request authentication collaborators for this simulation.
45
54
  * @internal
@@ -94,6 +103,7 @@ export class SimAwsServiceFactory {
94
103
  shadowableServiceHosts: this.registries.shadowableServiceHosts,
95
104
  iamRegistry: this.iamRegistry,
96
105
  accessKeyRegistry: this.requestAuth.accessKeyRegistry,
106
+ scpResolver: this.organizations,
97
107
  });
98
108
  this.accountRegionServices = new SimAwsAccountRegionServiceBuilder({
99
109
  simAws: properties.simAws,