diff --git a/.projen/deps.json b/.projen/deps.json
index 94566f2..fab3298 100644
--- a/.projen/deps.json
+++ b/.projen/deps.json
@@ -22,7 +22,11 @@
},
{
"name": "aws-cdk-lib",
- "version": "2.1.0",
+ "version": "2.58.0",
+ "type": "build"
+ },
+ {
+ "name": "aws-sdk",
"type": "build"
},
{
@@ -112,7 +116,7 @@
},
{
"name": "aws-cdk-lib",
- "version": "^2.1.0",
+ "version": "^2.58.0",
"type": "peer"
},
{
diff --git a/.projenrc.js b/.projenrc.js
index 99d02df..1fa691a 100644
--- a/.projenrc.js
+++ b/.projenrc.js
@@ -2,14 +2,21 @@ const { awscdk } = require('projen');
const project = new awscdk.AwsCdkConstructLibrary({
author: 'John Ferlito',
authorAddress: 'johnf@inodes.org',
- cdkVersion: '2.1.0',
+ cdkVersion: '2.58.0',
defaultReleaseBranch: 'main',
name: 'cdk-cross-account-route53',
+ description: 'CDK Construct to allow creation of Route 53 records in a different account',
repositoryUrl: 'https://github.com/johnf/cdk-cross-account-route53.git',
-
- // deps: [], /* Runtime dependencies of this module. */
- // description: undefined, /* The description is just a string that helps people understand the purpose of the package. */
- // devDeps: [], /* Build dependencies for this module. */
- // packageName: undefined, /* The "name" in package.json. */
+ keywords: [
+ 'aws',
+ 'aws-cdk',
+ 'awscdk',
+ 'cdk',
+ 'route53',
+ 'cross-account',
+ 'role',
+ 'records',
+ ],
+ devDeps: ['aws-sdk'],
});
-project.synth();
\ No newline at end of file
+project.synth();
diff --git a/API.md b/API.md
new file mode 100644
index 0000000..b37588c
--- /dev/null
+++ b/API.md
@@ -0,0 +1,378 @@
+# API Reference
+
+## Constructs
+
+### CrossAccountRoute53RecordSet
+
+#### Initializers
+
+```typescript
+import { CrossAccountRoute53RecordSet } from 'cdk-cross-account-route53'
+
+new CrossAccountRoute53RecordSet(scope: Construct, id: string, props: CrossAccountRoute53RecordSetProps)
+```
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| scope
| constructs.Construct
| *No description.* |
+| id
| string
| *No description.* |
+| props
| CrossAccountRoute53RecordSetProps
| *No description.* |
+
+---
+
+##### `scope`Required
+
+- *Type:* constructs.Construct
+
+---
+
+##### `id`Required
+
+- *Type:* string
+
+---
+
+##### `props`Required
+
+- *Type:* CrossAccountRoute53RecordSetProps
+
+---
+
+#### Methods
+
+| **Name** | **Description** |
+| --- | --- |
+| toString
| Returns a string representation of this construct. |
+
+---
+
+##### `toString`
+
+```typescript
+public toString(): string
+```
+
+Returns a string representation of this construct.
+
+#### Static Functions
+
+| **Name** | **Description** |
+| --- | --- |
+| isConstruct
| Checks if `x` is a construct. |
+
+---
+
+##### ~~`isConstruct`~~
+
+```typescript
+import { CrossAccountRoute53RecordSet } from 'cdk-cross-account-route53'
+
+CrossAccountRoute53RecordSet.isConstruct(x: any)
+```
+
+Checks if `x` is a construct.
+
+###### `x`Required
+
+- *Type:* any
+
+Any object.
+
+---
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| node
| constructs.Node
| The tree node. |
+
+---
+
+##### `node`Required
+
+```typescript
+public readonly node: Node;
+```
+
+- *Type:* constructs.Node
+
+The tree node.
+
+---
+
+
+### CrossAccountRoute53Role
+
+#### Initializers
+
+```typescript
+import { CrossAccountRoute53Role } from 'cdk-cross-account-route53'
+
+new CrossAccountRoute53Role(scope: Construct, id: string, props: CrossAccountRoute53RoleProps)
+```
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| scope
| constructs.Construct
| *No description.* |
+| id
| string
| *No description.* |
+| props
| CrossAccountRoute53RoleProps
| *No description.* |
+
+---
+
+##### `scope`Required
+
+- *Type:* constructs.Construct
+
+---
+
+##### `id`Required
+
+- *Type:* string
+
+---
+
+##### `props`Required
+
+- *Type:* CrossAccountRoute53RoleProps
+
+---
+
+#### Methods
+
+| **Name** | **Description** |
+| --- | --- |
+| toString
| Returns a string representation of this construct. |
+
+---
+
+##### `toString`
+
+```typescript
+public toString(): string
+```
+
+Returns a string representation of this construct.
+
+#### Static Functions
+
+| **Name** | **Description** |
+| --- | --- |
+| isConstruct
| Checks if `x` is a construct. |
+
+---
+
+##### ~~`isConstruct`~~
+
+```typescript
+import { CrossAccountRoute53Role } from 'cdk-cross-account-route53'
+
+CrossAccountRoute53Role.isConstruct(x: any)
+```
+
+Checks if `x` is a construct.
+
+###### `x`Required
+
+- *Type:* any
+
+Any object.
+
+---
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| node
| constructs.Node
| The tree node. |
+
+---
+
+##### `node`Required
+
+```typescript
+public readonly node: Node;
+```
+
+- *Type:* constructs.Node
+
+The tree node.
+
+---
+
+
+## Structs
+
+### CrossAccountRoute53RecordSetProps
+
+#### Initializer
+
+```typescript
+import { CrossAccountRoute53RecordSetProps } from 'cdk-cross-account-route53'
+
+const crossAccountRoute53RecordSetProps: CrossAccountRoute53RecordSetProps = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| delegationRoleAccount
| string
| *No description.* |
+| delegationRoleName
| string
| *No description.* |
+| hostedZoneId
| string
| *No description.* |
+| resourceRecordSets
| any
| *No description.* |
+
+---
+
+##### `delegationRoleAccount`Required
+
+```typescript
+public readonly delegationRoleAccount: string;
+```
+
+- *Type:* string
+
+---
+
+##### `delegationRoleName`Required
+
+```typescript
+public readonly delegationRoleName: string;
+```
+
+- *Type:* string
+
+---
+
+##### `hostedZoneId`Required
+
+```typescript
+public readonly hostedZoneId: string;
+```
+
+- *Type:* string
+
+---
+
+##### `resourceRecordSets`Required
+
+```typescript
+public readonly resourceRecordSets: any;
+```
+
+- *Type:* any
+
+---
+
+### CrossAccountRoute53RoleProps
+
+#### Initializer
+
+```typescript
+import { CrossAccountRoute53RoleProps } from 'cdk-cross-account-route53'
+
+const crossAccountRoute53RoleProps: CrossAccountRoute53RoleProps = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| assumedBy
| aws-cdk-lib.aws_iam.IPrincipal
| *No description.* |
+| records
| CrossAccountRoute53RolePropsRecord[]
| *No description.* |
+| roleName
| string
| *No description.* |
+| zone
| aws-cdk-lib.aws_route53.IHostedZone
| *No description.* |
+
+---
+
+##### `assumedBy`Required
+
+```typescript
+public readonly assumedBy: IPrincipal;
+```
+
+- *Type:* aws-cdk-lib.aws_iam.IPrincipal
+
+---
+
+##### `records`Required
+
+```typescript
+public readonly records: CrossAccountRoute53RolePropsRecord[];
+```
+
+- *Type:* CrossAccountRoute53RolePropsRecord[]
+
+---
+
+##### `roleName`Required
+
+```typescript
+public readonly roleName: string;
+```
+
+- *Type:* string
+
+---
+
+##### `zone`Required
+
+```typescript
+public readonly zone: IHostedZone;
+```
+
+- *Type:* aws-cdk-lib.aws_route53.IHostedZone
+
+---
+
+### CrossAccountRoute53RolePropsRecord
+
+#### Initializer
+
+```typescript
+import { CrossAccountRoute53RolePropsRecord } from 'cdk-cross-account-route53'
+
+const crossAccountRoute53RolePropsRecord: CrossAccountRoute53RolePropsRecord = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| domainNames
| string \| string[]
| *No description.* |
+| actions
| string[]
| *No description.* |
+| types
| string[]
| *No description.* |
+
+---
+
+##### `domainNames`Required
+
+```typescript
+public readonly domainNames: string | string[];
+```
+
+- *Type:* string | string[]
+
+---
+
+##### `actions`Optional
+
+```typescript
+public readonly actions: string[];
+```
+
+- *Type:* string[]
+
+---
+
+##### `types`Optional
+
+```typescript
+public readonly types: string[];
+```
+
+- *Type:* string[]
+
+---
+
+
+
diff --git a/README.md b/README.md
index b3fa7dd..d436746 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,119 @@
-# replace this
\ No newline at end of file
+# AWS CDK Cross Account Route53
+
+AWS [CDK](https://aws.amazon.com/cdk/) Constructs that define:
+- IAM role that can be used to allow discrete Route53 Record changes
+- Cross Account Record construct to create Route53 cross account Route53 records
+
+These constructs allow you to create Route53 records where the zone exists in a separate AWS account to the Cloudformation Stack.
+
+## Getting started
+
+```shell
+yarn add cdk-cross-account-route53
+```
+
+First create the role in the stack for the AWS account which contains the hosted zone.
+
+```typescript
+// DNS Stack
+const zone = new route53.PublicHostedZone(this, 'HostedZone', {
+ zoneName: 'example.com',
+});
+
+new CrossAccountRoute53Role(this, 'WebRoute53Role', {
+ roleName: 'WebRoute53Role',
+ assumedBy: new iam.AccountPrincipal('22222222'), // Web Stack Account
+ zone,
+ records: [{ domainNames: 'www.example.com' }],
+ });
+```
+
+Then in the child stack create the records
+
+```typescript
+const hostedZoneId = 'Z12345'; // ID of the zone in the other account
+
+const distribution = new cloudfront.Distribution(this, 'Distribution', {
+ domainNames: ['example.com'],
+});
+
+new CrossAccountRoute53RecordSet(this, 'ARecord', {
+ delegationRoleName: 'WebRoute53Role',
+ delegationRoleAccount: '111111111', // The account that contains the zone and role
+ hostedZoneId,
+ resourceRecordSets: [{
+ Name: `example.com`,
+ Type: 'A',
+ AliasTarget: {
+ DNSName: distribution.distributionDomainName,
+ HostedZoneId: 'Z2FDTNDATAQYW2', // Cloudfront Hosted Zone Id
+ EvaluateTargetHealth: false,
+ },
+ }],
+});
+```
+
+## CrossAccountRoute53Role
+
+### Initializer
+```typescript
+new CrossAccountRoute53Role(scope: Construct, id: string, props: CrossAccountRoute53RoleProps)
+```
+
+*Parameters*
+
+* **scope** Construct
+* **id** string
+* **props** CrossAccountRoute53RoleProps
+
+### Construct Props
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| roleName | `string` | The role name |
+| assumedBy | `iam.IPrincipal` | The principals that are allowed to assume the role |
+| zone | `route53.IHostedZone` | The hosted zone. |
+| records | `CrossAccountRoute53RolePropsRecord[]` | The records that can be created by this role |
+
+### CrossAccountRoute53RolePropsRecords
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| domainNames | `string \| string[]` | The names of the records that can be created or changed |
+| types | `route53.RecordType[]` | The typepsof records that can be created. Default `['A', 'AAAA']` |
+| actions | `'CREATE' \| 'UPSERT' \| 'DELETE'` | The allowed actions. Default `['CREATE', 'UPSERT', 'DELETE']` |
+
+## CrossAccountRoute53RecordSet
+
+### Initializer
+```typescript
+new CrossAccountRoute53RecordSet(scope: Construct, id: string, props: CrossAccountRoute53RecordSetProps)
+```
+
+*Parameters*
+
+* **scope** Construct
+* **id** string
+* **props** CrossAccountRoute53RecordSet
+
+### Construct Props
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| delegationRoleName | `string` | The role name created in the account with the hosted zone |
+| delegationRoleAccount | `string` | The account identfier of the account with the hosted zone |
+| hostedZoneId | `string` | The hosted zoned id |
+| resourceRecordSets | `Route53.ResourceRecordSets` | The changes to be applied. These are in the same format as taken by [ChangeResourceRecordSets Action](https://docs.aws.amazon.com/Route53/latest/APIReference/API_ResourceRecordSet.html) |
+
+## Development Status
+
+These constructs will stay in `v0.x.x` for a while, to allow easier bug fixing & breaking changes _if absolutely needed_.
+Once bugs are fixed (if any), the constructs will be published with `v1` major version and will be marked as stable.
+
+Only typescript has been tested.
+
+## Development
+
+* `npm run build` compile typescript to js
+* `npm run watch` watch for changes and compile
+* `npm run test` perform the jest unit tests
diff --git a/package.json b/package.json
index 8f97092..2676d6c 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,6 @@
{
"name": "cdk-cross-account-route53",
+ "description": "CDK Construct to allow creation of Route 53 records in a different account",
"repository": {
"type": "git",
"url": "https://github.com/johnf/cdk-cross-account-route53.git"
@@ -38,7 +39,8 @@
"@types/node": "^14",
"@typescript-eslint/eslint-plugin": "^5",
"@typescript-eslint/parser": "^5",
- "aws-cdk-lib": "2.1.0",
+ "aws-cdk-lib": "2.58.0",
+ "aws-sdk": "^2.1286.0",
"constructs": "10.0.5",
"eslint": "^8",
"eslint-import-resolver-node": "^0.3.6",
@@ -58,11 +60,18 @@
"typescript": "^4.9.4"
},
"peerDependencies": {
- "aws-cdk-lib": "^2.1.0",
+ "aws-cdk-lib": "^2.58.0",
"constructs": "^10.0.5"
},
"keywords": [
- "cdk"
+ "aws",
+ "aws-cdk",
+ "awscdk",
+ "cdk",
+ "cross-account",
+ "records",
+ "role",
+ "route53"
],
"main": "lib/index.js",
"license": "Apache-2.0",
diff --git a/src/cross-account-record-set-handler/index.ts b/src/cross-account-record-set-handler/index.ts
new file mode 100644
index 0000000..7ca566a
--- /dev/null
+++ b/src/cross-account-record-set-handler/index.ts
@@ -0,0 +1,64 @@
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { Credentials, Route53, STS } from 'aws-sdk';
+
+interface ResourceProperties {
+ AssumeRoleArn: string;
+ HostedZoneId: string;
+ ResourceRecordSets: Route53.ResourceRecordSet[];
+}
+
+export async function handler(event: any /* : AWSLambda.CloudFormationCustomResourceEvent */) {
+ const resourceProps = event.ResourceProperties as unknown as ResourceProperties;
+
+ switch (event.RequestType) {
+ case 'Create':
+ case 'Update':
+ return cfnEventHandler(resourceProps, false);
+ case 'Delete':
+ return cfnEventHandler(resourceProps, true);
+ }
+}
+
+async function cfnEventHandler(props: ResourceProperties, isDeleteEvent: boolean) {
+ const {
+ AssumeRoleArn,
+ HostedZoneId,
+ } = props;
+
+ const credentials = await getCrossAccountCredentials(AssumeRoleArn);
+ const route53 = new Route53({ credentials });
+
+ const Changes = props.ResourceRecordSets.map((set) => ({
+ Action: isDeleteEvent ? 'DELETE' : 'UPSERT',
+ ResourceRecordSet: set,
+ }));
+
+ await route53.changeResourceRecordSets({
+ HostedZoneId,
+ ChangeBatch: {
+ Changes,
+ },
+ }).promise();
+}
+
+async function getCrossAccountCredentials(roleArn: string): Promise {
+ const sts = new STS();
+ const timestamp = (new Date()).getTime();
+
+ const { Credentials: assumedCredentials } = await sts
+ .assumeRole({
+ RoleArn: roleArn,
+ RoleSessionName: `cross-account-record-set-${timestamp}`,
+ })
+ .promise();
+
+ if (!assumedCredentials) {
+ throw Error('Error getting assume role credentials');
+ }
+
+ return new Credentials({
+ accessKeyId: assumedCredentials.AccessKeyId,
+ secretAccessKey: assumedCredentials.SecretAccessKey,
+ sessionToken: assumedCredentials.SessionToken,
+ });
+}
diff --git a/src/index.ts b/src/index.ts
index 92c94b8..7324ea2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,131 @@
-export class Hello {
- public sayHello() {
- return 'hello, world!';
+import * as path from 'path';
+
+import { CustomResource, CustomResourceProvider, CustomResourceProviderRuntime, Stack } from 'aws-cdk-lib';
+
+import * as iam from 'aws-cdk-lib/aws-iam';
+import * as route53 from 'aws-cdk-lib/aws-route53';
+import { Construct } from 'constructs';
+// import { Route53 } from 'aws-sdk';
+
+export type CrossAccountRoute53RolePropsRecordAction = 'CREATE' | 'UPSERT' | 'DELETE';
+
+export interface CrossAccountRoute53RolePropsRecord {
+ readonly domainNames: string | string[];
+ readonly types?: (keyof typeof route53.RecordType)[];
+ readonly actions?: CrossAccountRoute53RolePropsRecordAction[];
+}
+
+export interface CrossAccountRoute53RoleProps {
+ readonly roleName: string;
+ readonly assumedBy: iam.IPrincipal;
+ readonly zone: route53.IHostedZone;
+ readonly records: CrossAccountRoute53RolePropsRecord[];
+}
+
+export class CrossAccountRoute53Role extends Construct {
+ constructor(scope: Construct, id: string, props: CrossAccountRoute53RoleProps) {
+ super(scope, id);
+
+ const { roleName, assumedBy, zone, records } = props;
+
+ const statements = records.map((record) => {
+ const domainNames = Array.isArray(record.domainNames) ? record.domainNames : [record.domainNames];
+
+ return new iam.PolicyStatement({
+ actions: ['route53:ChangeResourceRecordSets'],
+ resources: [zone.hostedZoneArn],
+ conditions: {
+ 'ForAllValues:StringEquals': {
+ 'route53:ChangeResourceRecordSetsRecordTypes': record.types || ['A', 'AAAA'],
+ 'route53:ChangeResourceRecordSetsActions': record.actions || ['CREATE', 'UPSERT', 'DELETE'],
+ 'route53:ChangeResourceRecordSetsNormalizedRecordNames': domainNames.map((domainName) => this.normaliseDomainName(domainName)),
+ },
+ },
+ });
+ });
+
+ statements.push(new iam.PolicyStatement({
+ actions: ['route53:ListHostedZonesByName'],
+ resources: [zone.hostedZoneArn],
+ }));
+
+ // Can we be more specific here?
+ statements.push(new iam.PolicyStatement({
+ actions: ['route53:GetChange'],
+ resources: ['*'],
+ }));
+
+ new iam.Role(this, id, {
+ roleName,
+ assumedBy,
+ inlinePolicies: {
+ delegation: new iam.PolicyDocument({ statements }),
+ },
+ });
}
-}
\ No newline at end of file
+
+ private normaliseDomainName(domainName: string): string {
+ return domainName
+ .replace(/\.$/, '')
+ .toLowerCase()
+ .split('').map((char) => {
+ if (char.match(/[a-z0-9_.-]/)) {
+ return char;
+ }
+
+ const octal = '000' + char.charCodeAt(0).toString(8);
+ return `\\${octal.substring(octal.length - 3)}`;
+ })
+ .join('');
+ }
+}
+
+export interface CrossAccountRoute53RecordSetProps {
+ readonly delegationRoleName: string;
+ readonly delegationRoleAccount: string;
+ readonly hostedZoneId: string;
+ readonly resourceRecordSets: any; // Route53.ResourceRecordSet[],
+}
+
+export class CrossAccountRoute53RecordSet extends Construct {
+ constructor(scope: Construct, id: string, props: CrossAccountRoute53RecordSetProps) {
+ super(scope, id);
+
+ const delegationRoleArn = Stack.of(this).formatArn({
+ region: '',
+ service: 'iam',
+ account: props.delegationRoleAccount,
+ resource: 'role',
+ resourceName: props.delegationRoleName,
+ });
+
+ const customResourceType = 'Custom::CrossAccountRoute53RecordSet';
+
+ const provider = CustomResourceProvider.getOrCreateProvider(this, customResourceType, {
+ codeDirectory: path.join(__dirname, 'cross-account-record-set-handler'),
+ runtime: CustomResourceProviderRuntime.NODEJS_16_X,
+ });
+
+ const role = iam.Role.fromRoleArn(this, 'cross-account-record-set-handler-role', provider.roleArn);
+
+ const addToPrinciplePolicyResult = role.addToPrincipalPolicy(new iam.PolicyStatement({
+ effect: iam.Effect.ALLOW,
+ actions: ['sts:AssumeRole'],
+ resources: [delegationRoleArn],
+ }));
+
+ const customResource = new CustomResource(this, 'CrossAccountRecordSetCustomResource', {
+ resourceType: customResourceType,
+ serviceToken: provider.serviceToken,
+ properties: {
+ AssumeRoleArn: delegationRoleArn,
+ HostedZoneId: props.hostedZoneId,
+ ResourceRecordSets: props.resourceRecordSets,
+ },
+ });
+
+ if (addToPrinciplePolicyResult.policyDependable) {
+ customResource.node.addDependency(addToPrinciplePolicyResult.policyDependable);
+ }
+ }
+}
diff --git a/test/cross-account-route53.test.ts b/test/cross-account-route53.test.ts
new file mode 100644
index 0000000..6c3df58
--- /dev/null
+++ b/test/cross-account-route53.test.ts
@@ -0,0 +1,85 @@
+import * as cdk from 'aws-cdk-lib';
+import { Capture, Template } from 'aws-cdk-lib/assertions';
+import * as iam from 'aws-cdk-lib/aws-iam';
+import * as route53 from 'aws-cdk-lib/aws-route53';
+import { CrossAccountRoute53Role, CrossAccountRoute53RoleProps } from '../lib/index';
+
+test('Role Created (defaults)', () => {
+ const app = new cdk.App();
+ const stack = new cdk.Stack(app, 'TestStack');
+
+ const props: CrossAccountRoute53RoleProps = {
+ roleName: 'TestRole',
+ assumedBy: new iam.AccountPrincipal('123456789012'),
+ zone: new route53.HostedZone(stack, 'HostedZone', { zoneName: 'inodes.org' }),
+ records: [
+ { domainNames: 'test.inodes.org' },
+ ],
+ };
+
+ new CrossAccountRoute53Role(stack, 'MyTestConstruct', props);
+
+ const template = Template.fromStack(stack);
+ const policies = new Capture();
+ // const assumedBy = new Capture();
+
+ template.hasResourceProperties('AWS::IAM::Role', {
+ RoleName: 'TestRole',
+ Policies: policies,
+ // AssumeRolePolicyDocument: assumedBy,
+ });
+ // console.debug(assumedBy.asString());
+
+ const policy = policies.asArray()[0];
+ const statements = policy.PolicyDocument.Statement;
+
+ const recordStatement = statements.find((statement: any) => statement.Action === 'route53:ChangeResourceRecordSets');
+
+ expect(recordStatement).toBeDefined();
+ const condition = recordStatement.Condition;
+ expect(condition).toBeDefined();
+
+ expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsRecordTypes']).toEqual(['A', 'AAAA']);
+ expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsActions']).toEqual(['CREATE', 'UPSERT', 'DELETE']);
+ expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsNormalizedRecordNames']).toEqual(['test.inodes.org']);
+});
+
+test('Role Created (custom)', () => {
+ const app = new cdk.App();
+ const stack = new cdk.Stack(app, 'TestStack');
+
+ const props: CrossAccountRoute53RoleProps = {
+ roleName: 'TestRole',
+ assumedBy: new iam.AccountPrincipal('123456789012'),
+ zone: new route53.HostedZone(stack, 'HostedZone', { zoneName: 'inodes.org' }),
+ records: [
+ { domainNames: 'test%.inodes.org.', types: ['TXT'], actions: ['UPSERT'] },
+ ],
+ };
+
+ new CrossAccountRoute53Role(stack, 'MyTestConstruct', props);
+
+ const template = Template.fromStack(stack);
+ const policies = new Capture();
+ // const assumedBy = new Capture();
+
+ template.hasResourceProperties('AWS::IAM::Role', {
+ RoleName: 'TestRole',
+ Policies: policies,
+ // AssumeRolePolicyDocument: assumedBy,
+ });
+ // console.debug(assumedBy.asString());
+
+ const policy = policies.asArray()[0];
+ const statements = policy.PolicyDocument.Statement;
+
+ const recordStatement = statements.find((statement: any) => statement.Action === 'route53:ChangeResourceRecordSets');
+
+ expect(recordStatement).toBeDefined();
+ const condition = recordStatement.Condition;
+ expect(condition).toBeDefined();
+
+ expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsRecordTypes']).toEqual(['TXT']);
+ expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsActions']).toEqual(['UPSERT']);
+ expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsNormalizedRecordNames']).toEqual(['test\\045.inodes.org']);
+});
diff --git a/test/hello.test.ts b/test/hello.test.ts
deleted file mode 100644
index acbacd4..0000000
--- a/test/hello.test.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { Hello } from '../src';
-
-test('hello', () => {
- expect(new Hello().sayHello()).toBe('hello, world!');
-});
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 02fb592..80d8008 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10,6 +10,21 @@
"@jridgewell/gen-mapping" "^0.1.0"
"@jridgewell/trace-mapping" "^0.3.9"
+"@aws-cdk/asset-awscli-v1@^2.2.30":
+ version "2.2.48"
+ resolved "https://registry.yarnpkg.com/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.48.tgz#b0aa6d082419f7e6562d3c268121facbcfdd3445"
+ integrity sha512-lBDe9flsUqKajlf3/7mCDqXdQp+onH5WCNBRSXyUHeXwLwJIVCVFEyW2WvPSxF1y6bgs9oG8AFAXLWRscomgrw==
+
+"@aws-cdk/asset-kubectl-v20@^2.1.1":
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz#d01c1efb867fb7f2cfd8c8b230b8eae16447e156"
+ integrity sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==
+
+"@aws-cdk/asset-node-proxy-agent-v5@^2.0.38":
+ version "2.0.38"
+ resolved "https://registry.yarnpkg.com/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.38.tgz#6765bef55f95220c52decb4adba8f75c1817b0f7"
+ integrity sha512-BBwAjORhuUkTGO3CxGS5Evcp5n20h9v06Sftn2R1DuSm8zIoUlPsNlI1HUk8XqYuoEI4aD7IKRQBLglv09ciJQ==
+
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
@@ -1219,21 +1234,45 @@ at-least-node@^1.0.0:
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
-aws-cdk-lib@2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.1.0.tgz#2497484cfd4e2eeaba99b070bbfa54486d52ae34"
- integrity sha512-W607G3aSrWpawpcqzIuUYKlU+grfvkbszyqikyVYqJgMHFCCQXq0S1ynPMzfQ49CwjlwZsu4LIsPM+dNR+Yj6g==
+available-typed-arrays@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
+ integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
+
+aws-cdk-lib@2.58.0:
+ version "2.58.0"
+ resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.58.0.tgz#83ab3b145db3c13d833b79c23dd98c71c0b7f23d"
+ integrity sha512-L5l5KvTO15ENhjB1aYi+Bl0CnWsOBN3ARb9E5qjXnaWQNMta0vWpSz/CjpkfHtJGXpyRXEg99vXOY5ocVMKrmw==
dependencies:
+ "@aws-cdk/asset-awscli-v1" "^2.2.30"
+ "@aws-cdk/asset-kubectl-v20" "^2.1.1"
+ "@aws-cdk/asset-node-proxy-agent-v5" "^2.0.38"
"@balena/dockerignore" "^1.0.2"
case "1.6.3"
fs-extra "^9.1.0"
- ignore "^5.1.9"
- jsonschema "^1.4.0"
- minimatch "^3.0.4"
+ ignore "^5.2.1"
+ jsonschema "^1.4.1"
+ minimatch "^3.1.2"
punycode "^2.1.1"
- semver "^7.3.5"
+ semver "^7.3.8"
yaml "1.10.2"
+aws-sdk@^2.1286.0:
+ version "2.1286.0"
+ resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1286.0.tgz#700d6081222a400c753aff640d679714a7ab3e87"
+ integrity sha512-CvkCD1+NSk2MPOutD2hEPhXDET/79w/gd9a359QWb9Ja0Fd4vVFXPkhlm1DTGzuwqFKGinpCMxDP4md7QPsVvw==
+ dependencies:
+ buffer "4.9.2"
+ events "1.1.1"
+ ieee754 "1.1.13"
+ jmespath "0.16.0"
+ querystring "0.2.0"
+ sax "1.2.1"
+ url "0.10.3"
+ util "^0.12.4"
+ uuid "8.0.0"
+ xml2js "0.4.19"
+
babel-jest@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444"
@@ -1300,6 +1339,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+base64-js@^1.0.2:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+ integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
boxen@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-7.0.1.tgz#cd84db4364a8bae65f1f016ce94a21ec2c832c16"
@@ -1370,6 +1414,15 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+buffer@4.9.2:
+ version "4.9.2"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
+ integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
+ dependencies:
+ base64-js "^1.0.2"
+ ieee754 "^1.1.4"
+ isarray "^1.0.0"
+
builtins@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9"
@@ -2418,6 +2471,11 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+events@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
+ integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==
+
execa@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
@@ -2562,6 +2620,13 @@ flatted@^3.1.0, flatted@^3.2.7:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
+for-each@^0.3.3:
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
+ integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==
+ dependencies:
+ is-callable "^1.1.3"
+
form-data-encoder@^2.1.2:
version "2.1.4"
resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5"
@@ -3064,6 +3129,16 @@ iconv-lite@^0.6.2:
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
+ieee754@1.1.13:
+ version "1.1.13"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
+ integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
+
+ieee754@^1.1.4:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+ integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
ignore-walk@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.0.tgz#1dd41c6eb4f661a49750a510a10c2cd934583fd8"
@@ -3071,7 +3146,7 @@ ignore-walk@^6.0.0:
dependencies:
minimatch "^5.0.1"
-ignore@^5.1.9, ignore@^5.2.0:
+ignore@^5.2.0, ignore@^5.2.1:
version "5.2.4"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
@@ -3159,6 +3234,14 @@ ip@^2.0.0:
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
+is-arguments@^1.0.4:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
+ integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
+ dependencies:
+ call-bind "^1.0.2"
+ has-tostringtag "^1.0.0"
+
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@@ -3179,7 +3262,7 @@ is-boolean-object@^1.1.0:
call-bind "^1.0.2"
has-tostringtag "^1.0.0"
-is-callable@^1.1.4, is-callable@^1.2.7:
+is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
@@ -3225,6 +3308,13 @@ is-generator-fn@^2.0.0:
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
+is-generator-function@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72"
+ integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==
+ dependencies:
+ has-tostringtag "^1.0.0"
+
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
@@ -3328,6 +3418,17 @@ is-text-path@^1.0.1:
dependencies:
text-extensions "^1.0.0"
+is-typed-array@^1.1.10, is-typed-array@^1.1.3:
+ version "1.1.10"
+ resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f"
+ integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==
+ dependencies:
+ available-typed-arrays "^1.0.5"
+ call-bind "^1.0.2"
+ for-each "^0.3.3"
+ gopd "^1.0.1"
+ has-tostringtag "^1.0.0"
+
is-typedarray@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@@ -3352,7 +3453,7 @@ is-yarn-global@^0.4.0:
resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.4.1.tgz#b312d902b313f81e4eaf98b6361ba2b45cd694bb"
integrity sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==
-isarray@~1.0.0:
+isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
@@ -3824,6 +3925,11 @@ jju@^1.1.0:
resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==
+jmespath@0.16.0:
+ version "0.16.0"
+ resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076"
+ integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==
+
js-sdsl@^4.1.4:
version "4.2.0"
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.2.0.tgz#278e98b7bea589b8baaf048c20aeb19eb7ad09d0"
@@ -4079,7 +4185,7 @@ jsonparse@^1.2.0, jsonparse@^1.3.1:
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
-jsonschema@^1.4.0:
+jsonschema@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab"
integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==
@@ -5104,6 +5210,11 @@ psl@^1.1.33:
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
+punycode@1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
+ integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==
+
punycode@^2.1.0, punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
@@ -5121,6 +5232,11 @@ q@^1.5.1:
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==
+querystring@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
+ integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==
+
querystringify@^2.1.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
@@ -5406,6 +5522,16 @@ safe-regex-test@^1.0.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+sax@1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
+ integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==
+
+sax@>=0.6.0:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+ integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
saxes@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d"
@@ -6175,11 +6301,35 @@ url-parse@^1.5.3:
querystringify "^2.1.1"
requires-port "^1.0.0"
+url@0.10.3:
+ version "0.10.3"
+ resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64"
+ integrity sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==
+ dependencies:
+ punycode "1.3.2"
+ querystring "0.2.0"
+
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+util@^0.12.4:
+ version "0.12.5"
+ resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc"
+ integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==
+ dependencies:
+ inherits "^2.0.3"
+ is-arguments "^1.0.4"
+ is-generator-function "^1.0.7"
+ is-typed-array "^1.1.3"
+ which-typed-array "^1.1.2"
+
+uuid@8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c"
+ integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==
+
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
@@ -6272,6 +6422,18 @@ which-boxed-primitive@^1.0.2:
is-string "^1.0.5"
is-symbol "^1.0.3"
+which-typed-array@^1.1.2:
+ version "1.1.9"
+ resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6"
+ integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==
+ dependencies:
+ available-typed-arrays "^1.0.5"
+ call-bind "^1.0.2"
+ for-each "^0.3.3"
+ gopd "^1.0.1"
+ has-tostringtag "^1.0.0"
+ is-typed-array "^1.1.10"
+
which@^2.0.1, which@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
@@ -6363,6 +6525,14 @@ xml-name-validator@^3.0.0:
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
+xml2js@0.4.19:
+ version "0.4.19"
+ resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
+ integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==
+ dependencies:
+ sax ">=0.6.0"
+ xmlbuilder "~9.0.1"
+
xml@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
@@ -6384,6 +6554,11 @@ xmlbuilder@^15.1.1:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"
integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==
+xmlbuilder@~9.0.1:
+ version "9.0.7"
+ resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
+ integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==
+
xmlchars@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"