feat: add more and more bps
This commit is contained in:
@ -31,6 +31,9 @@
|
|||||||
"@aws-sdk/client-rds": "^3.716.0",
|
"@aws-sdk/client-rds": "^3.716.0",
|
||||||
"@aws-sdk/client-s3": "^3.717.0",
|
"@aws-sdk/client-s3": "^3.717.0",
|
||||||
"@aws-sdk/client-s3-control": "^3.716.0",
|
"@aws-sdk/client-s3-control": "^3.716.0",
|
||||||
|
"@aws-sdk/client-secrets-manager": "^3.716.0",
|
||||||
|
"@aws-sdk/client-securityhub": "^3.716.0",
|
||||||
|
"@aws-sdk/client-sns": "^3.716.0",
|
||||||
"@aws-sdk/client-ssm": "^3.716.0",
|
"@aws-sdk/client-ssm": "^3.716.0",
|
||||||
"@aws-sdk/client-sts": "^3.716.0",
|
"@aws-sdk/client-sts": "^3.716.0",
|
||||||
"@aws-sdk/client-wafv2": "^3.716.0",
|
"@aws-sdk/client-wafv2": "^3.716.0",
|
||||||
|
163
pnpm-lock.yaml
generated
163
pnpm-lock.yaml
generated
@ -74,6 +74,15 @@ importers:
|
|||||||
'@aws-sdk/client-s3-control':
|
'@aws-sdk/client-s3-control':
|
||||||
specifier: ^3.716.0
|
specifier: ^3.716.0
|
||||||
version: 3.716.0
|
version: 3.716.0
|
||||||
|
'@aws-sdk/client-secrets-manager':
|
||||||
|
specifier: ^3.716.0
|
||||||
|
version: 3.716.0
|
||||||
|
'@aws-sdk/client-securityhub':
|
||||||
|
specifier: ^3.716.0
|
||||||
|
version: 3.716.0
|
||||||
|
'@aws-sdk/client-sns':
|
||||||
|
specifier: ^3.716.0
|
||||||
|
version: 3.716.0
|
||||||
'@aws-sdk/client-ssm':
|
'@aws-sdk/client-ssm':
|
||||||
specifier: ^3.716.0
|
specifier: ^3.716.0
|
||||||
version: 3.716.0
|
version: 3.716.0
|
||||||
@ -222,6 +231,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-jzaH8IskAXVnqlZ3/H/ROwrB2HCnq/atlN7Hi7FIfjWvMPf5nfcJKfzJ1MXFX0EQR5qO6X4TbK7rgi7Bjw9NjQ==}
|
resolution: {integrity: sha512-jzaH8IskAXVnqlZ3/H/ROwrB2HCnq/atlN7Hi7FIfjWvMPf5nfcJKfzJ1MXFX0EQR5qO6X4TbK7rgi7Bjw9NjQ==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/client-secrets-manager@3.716.0':
|
||||||
|
resolution: {integrity: sha512-j2JboOSR3PMoT5msr4uIMwkIm1owzkqgWI8i40IPDa1oeJXmZIx/xkCQq6Hxu5Ve1b2xtrw/8k1LN+TMCvuIfA==}
|
||||||
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/client-securityhub@3.716.0':
|
||||||
|
resolution: {integrity: sha512-/xGd2NQd7CtUDquRZvXbIDcMoBKaJmvwnjujVnmwth/6yC0gAVsBp6yeBSHOszI6L4mMXmSQ2iBglgnexpVlDQ==}
|
||||||
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
|
'@aws-sdk/client-sns@3.716.0':
|
||||||
|
resolution: {integrity: sha512-Qg7EqlS83yiHRpfbhlyanRQ5aKmj1M8K7OJcDYgjI8FNf4YlS/YKJkv02SQVdW0lFLT2adceUXBjdAQXlbXC7g==}
|
||||||
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
'@aws-sdk/client-ssm@3.716.0':
|
'@aws-sdk/client-ssm@3.716.0':
|
||||||
resolution: {integrity: sha512-da2wTUBCLGRoQf5Ahm/LuUIR/OkQ09kaX7yYRC2Vw+TOcMXbozJSzXbm99SXsOL4u8a8PRq+Vwfptc36e18Feg==}
|
resolution: {integrity: sha512-da2wTUBCLGRoQf5Ahm/LuUIR/OkQ09kaX7yYRC2Vw+TOcMXbozJSzXbm99SXsOL4u8a8PRq+Vwfptc36e18Feg==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
@ -2237,6 +2258,146 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- aws-crt
|
- aws-crt
|
||||||
|
|
||||||
|
'@aws-sdk/client-secrets-manager@3.716.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/sha256-browser': 5.2.0
|
||||||
|
'@aws-crypto/sha256-js': 5.2.0
|
||||||
|
'@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0)
|
||||||
|
'@aws-sdk/client-sts': 3.716.0
|
||||||
|
'@aws-sdk/core': 3.716.0
|
||||||
|
'@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0)
|
||||||
|
'@aws-sdk/middleware-host-header': 3.714.0
|
||||||
|
'@aws-sdk/middleware-logger': 3.714.0
|
||||||
|
'@aws-sdk/middleware-recursion-detection': 3.714.0
|
||||||
|
'@aws-sdk/middleware-user-agent': 3.716.0
|
||||||
|
'@aws-sdk/region-config-resolver': 3.714.0
|
||||||
|
'@aws-sdk/types': 3.714.0
|
||||||
|
'@aws-sdk/util-endpoints': 3.714.0
|
||||||
|
'@aws-sdk/util-user-agent-browser': 3.714.0
|
||||||
|
'@aws-sdk/util-user-agent-node': 3.716.0
|
||||||
|
'@smithy/config-resolver': 3.0.13
|
||||||
|
'@smithy/core': 2.5.6
|
||||||
|
'@smithy/fetch-http-handler': 4.1.2
|
||||||
|
'@smithy/hash-node': 3.0.11
|
||||||
|
'@smithy/invalid-dependency': 3.0.11
|
||||||
|
'@smithy/middleware-content-length': 3.0.13
|
||||||
|
'@smithy/middleware-endpoint': 3.2.6
|
||||||
|
'@smithy/middleware-retry': 3.0.31
|
||||||
|
'@smithy/middleware-serde': 3.0.11
|
||||||
|
'@smithy/middleware-stack': 3.0.11
|
||||||
|
'@smithy/node-config-provider': 3.1.12
|
||||||
|
'@smithy/node-http-handler': 3.3.3
|
||||||
|
'@smithy/protocol-http': 4.1.8
|
||||||
|
'@smithy/smithy-client': 3.5.1
|
||||||
|
'@smithy/types': 3.7.2
|
||||||
|
'@smithy/url-parser': 3.0.11
|
||||||
|
'@smithy/util-base64': 3.0.0
|
||||||
|
'@smithy/util-body-length-browser': 3.0.0
|
||||||
|
'@smithy/util-body-length-node': 3.0.0
|
||||||
|
'@smithy/util-defaults-mode-browser': 3.0.31
|
||||||
|
'@smithy/util-defaults-mode-node': 3.0.31
|
||||||
|
'@smithy/util-endpoints': 2.1.7
|
||||||
|
'@smithy/util-middleware': 3.0.11
|
||||||
|
'@smithy/util-retry': 3.0.11
|
||||||
|
'@smithy/util-utf8': 3.0.0
|
||||||
|
'@types/uuid': 9.0.8
|
||||||
|
tslib: 2.8.1
|
||||||
|
uuid: 9.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- aws-crt
|
||||||
|
|
||||||
|
'@aws-sdk/client-securityhub@3.716.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/sha256-browser': 5.2.0
|
||||||
|
'@aws-crypto/sha256-js': 5.2.0
|
||||||
|
'@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0)
|
||||||
|
'@aws-sdk/client-sts': 3.716.0
|
||||||
|
'@aws-sdk/core': 3.716.0
|
||||||
|
'@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0)
|
||||||
|
'@aws-sdk/middleware-host-header': 3.714.0
|
||||||
|
'@aws-sdk/middleware-logger': 3.714.0
|
||||||
|
'@aws-sdk/middleware-recursion-detection': 3.714.0
|
||||||
|
'@aws-sdk/middleware-user-agent': 3.716.0
|
||||||
|
'@aws-sdk/region-config-resolver': 3.714.0
|
||||||
|
'@aws-sdk/types': 3.714.0
|
||||||
|
'@aws-sdk/util-endpoints': 3.714.0
|
||||||
|
'@aws-sdk/util-user-agent-browser': 3.714.0
|
||||||
|
'@aws-sdk/util-user-agent-node': 3.716.0
|
||||||
|
'@smithy/config-resolver': 3.0.13
|
||||||
|
'@smithy/core': 2.5.6
|
||||||
|
'@smithy/fetch-http-handler': 4.1.2
|
||||||
|
'@smithy/hash-node': 3.0.11
|
||||||
|
'@smithy/invalid-dependency': 3.0.11
|
||||||
|
'@smithy/middleware-content-length': 3.0.13
|
||||||
|
'@smithy/middleware-endpoint': 3.2.6
|
||||||
|
'@smithy/middleware-retry': 3.0.31
|
||||||
|
'@smithy/middleware-serde': 3.0.11
|
||||||
|
'@smithy/middleware-stack': 3.0.11
|
||||||
|
'@smithy/node-config-provider': 3.1.12
|
||||||
|
'@smithy/node-http-handler': 3.3.3
|
||||||
|
'@smithy/protocol-http': 4.1.8
|
||||||
|
'@smithy/smithy-client': 3.5.1
|
||||||
|
'@smithy/types': 3.7.2
|
||||||
|
'@smithy/url-parser': 3.0.11
|
||||||
|
'@smithy/util-base64': 3.0.0
|
||||||
|
'@smithy/util-body-length-browser': 3.0.0
|
||||||
|
'@smithy/util-body-length-node': 3.0.0
|
||||||
|
'@smithy/util-defaults-mode-browser': 3.0.31
|
||||||
|
'@smithy/util-defaults-mode-node': 3.0.31
|
||||||
|
'@smithy/util-endpoints': 2.1.7
|
||||||
|
'@smithy/util-middleware': 3.0.11
|
||||||
|
'@smithy/util-retry': 3.0.11
|
||||||
|
'@smithy/util-utf8': 3.0.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- aws-crt
|
||||||
|
|
||||||
|
'@aws-sdk/client-sns@3.716.0':
|
||||||
|
dependencies:
|
||||||
|
'@aws-crypto/sha256-browser': 5.2.0
|
||||||
|
'@aws-crypto/sha256-js': 5.2.0
|
||||||
|
'@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0)
|
||||||
|
'@aws-sdk/client-sts': 3.716.0
|
||||||
|
'@aws-sdk/core': 3.716.0
|
||||||
|
'@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0)
|
||||||
|
'@aws-sdk/middleware-host-header': 3.714.0
|
||||||
|
'@aws-sdk/middleware-logger': 3.714.0
|
||||||
|
'@aws-sdk/middleware-recursion-detection': 3.714.0
|
||||||
|
'@aws-sdk/middleware-user-agent': 3.716.0
|
||||||
|
'@aws-sdk/region-config-resolver': 3.714.0
|
||||||
|
'@aws-sdk/types': 3.714.0
|
||||||
|
'@aws-sdk/util-endpoints': 3.714.0
|
||||||
|
'@aws-sdk/util-user-agent-browser': 3.714.0
|
||||||
|
'@aws-sdk/util-user-agent-node': 3.716.0
|
||||||
|
'@smithy/config-resolver': 3.0.13
|
||||||
|
'@smithy/core': 2.5.6
|
||||||
|
'@smithy/fetch-http-handler': 4.1.2
|
||||||
|
'@smithy/hash-node': 3.0.11
|
||||||
|
'@smithy/invalid-dependency': 3.0.11
|
||||||
|
'@smithy/middleware-content-length': 3.0.13
|
||||||
|
'@smithy/middleware-endpoint': 3.2.6
|
||||||
|
'@smithy/middleware-retry': 3.0.31
|
||||||
|
'@smithy/middleware-serde': 3.0.11
|
||||||
|
'@smithy/middleware-stack': 3.0.11
|
||||||
|
'@smithy/node-config-provider': 3.1.12
|
||||||
|
'@smithy/node-http-handler': 3.3.3
|
||||||
|
'@smithy/protocol-http': 4.1.8
|
||||||
|
'@smithy/smithy-client': 3.5.1
|
||||||
|
'@smithy/types': 3.7.2
|
||||||
|
'@smithy/url-parser': 3.0.11
|
||||||
|
'@smithy/util-base64': 3.0.0
|
||||||
|
'@smithy/util-body-length-browser': 3.0.0
|
||||||
|
'@smithy/util-body-length-node': 3.0.0
|
||||||
|
'@smithy/util-defaults-mode-browser': 3.0.31
|
||||||
|
'@smithy/util-defaults-mode-node': 3.0.31
|
||||||
|
'@smithy/util-endpoints': 2.1.7
|
||||||
|
'@smithy/util-middleware': 3.0.11
|
||||||
|
'@smithy/util-retry': 3.0.11
|
||||||
|
'@smithy/util-utf8': 3.0.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- aws-crt
|
||||||
|
|
||||||
'@aws-sdk/client-ssm@3.716.0':
|
'@aws-sdk/client-ssm@3.716.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@aws-crypto/sha256-browser': 5.2.0
|
'@aws-crypto/sha256-browser': 5.2.0
|
||||||
@ -2703,7 +2864,7 @@ snapshots:
|
|||||||
'@aws-sdk/core': 3.716.0
|
'@aws-sdk/core': 3.716.0
|
||||||
'@aws-sdk/types': 3.714.0
|
'@aws-sdk/types': 3.714.0
|
||||||
'@aws-sdk/util-endpoints': 3.714.0
|
'@aws-sdk/util-endpoints': 3.714.0
|
||||||
'@smithy/core': 2.5.5
|
'@smithy/core': 2.5.6
|
||||||
'@smithy/protocol-http': 4.1.8
|
'@smithy/protocol-http': 4.1.8
|
||||||
'@smithy/types': 3.7.2
|
'@smithy/types': 3.7.2
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
@ -2,7 +2,15 @@ export interface BPSet {
|
|||||||
check: () => Promise<{
|
check: () => Promise<{
|
||||||
compliantResources: string[]
|
compliantResources: string[]
|
||||||
nonCompliantResources: string[]
|
nonCompliantResources: string[]
|
||||||
requiredParametersForFix: {name: string}[]
|
requiredParametersForFix: {
|
||||||
|
name: string
|
||||||
|
}[]
|
||||||
}>,
|
}>,
|
||||||
fix: (nonCompliantResources: string[], requiredParametersForFix: {name: string, value: string}[]) => Promise<void>
|
fix: (
|
||||||
|
nonCompliantResources: string[],
|
||||||
|
requiredParametersForFix: {
|
||||||
|
name: string,
|
||||||
|
value: string
|
||||||
|
}[]
|
||||||
|
) => Promise<void>
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
import {
|
||||||
|
SecretsManagerClient,
|
||||||
|
ListSecretsCommand,
|
||||||
|
RotateSecretCommand,
|
||||||
|
UpdateSecretCommand
|
||||||
|
} from '@aws-sdk/client-secrets-manager'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class SecretsManagerRotationEnabledCheck implements BPSet {
|
||||||
|
private readonly client = new SecretsManagerClient({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
private readonly getSecrets = async () => {
|
||||||
|
const response = await this.memoClient.send(new ListSecretsCommand({}))
|
||||||
|
return response.SecretList || []
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
const secrets = await this.getSecrets()
|
||||||
|
|
||||||
|
for (const secret of secrets) {
|
||||||
|
if (secret.RotationEnabled) {
|
||||||
|
compliantResources.push(secret.ARN!)
|
||||||
|
} else {
|
||||||
|
nonCompliantResources.push(secret.ARN!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const arn of nonCompliantResources) {
|
||||||
|
await this.client.send(
|
||||||
|
new RotateSecretCommand({
|
||||||
|
SecretId: arn
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
import {
|
||||||
|
SecretsManagerClient,
|
||||||
|
ListSecretsCommand,
|
||||||
|
RotateSecretCommand
|
||||||
|
} from '@aws-sdk/client-secrets-manager'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class SecretsManagerScheduledRotationSuccessCheck implements BPSet {
|
||||||
|
private readonly client = new SecretsManagerClient({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
private readonly getSecrets = async () => {
|
||||||
|
const response = await this.memoClient.send(new ListSecretsCommand({}))
|
||||||
|
return response.SecretList || []
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
const secrets = await this.getSecrets()
|
||||||
|
|
||||||
|
for (const secret of secrets) {
|
||||||
|
if (secret.RotationEnabled) {
|
||||||
|
const now = new Date()
|
||||||
|
const lastRotated = secret.LastRotatedDate ? new Date(secret.LastRotatedDate) : undefined
|
||||||
|
const rotationPeriod = secret.RotationRules?.AutomaticallyAfterDays
|
||||||
|
? secret.RotationRules.AutomaticallyAfterDays + 2
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
if (!lastRotated || !rotationPeriod || now.getTime() - lastRotated.getTime() > rotationPeriod * 24 * 60 * 60 * 1000) {
|
||||||
|
nonCompliantResources.push(secret.ARN!)
|
||||||
|
} else {
|
||||||
|
compliantResources.push(secret.ARN!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const arn of nonCompliantResources) {
|
||||||
|
await this.client.send(
|
||||||
|
new RotateSecretCommand({
|
||||||
|
SecretId: arn
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
import {
|
||||||
|
SecretsManagerClient,
|
||||||
|
ListSecretsCommand,
|
||||||
|
RotateSecretCommand
|
||||||
|
} from '@aws-sdk/client-secrets-manager'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class SecretsManagerSecretPeriodicRotation implements BPSet {
|
||||||
|
private readonly client = new SecretsManagerClient({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
private readonly getSecrets = async () => {
|
||||||
|
const response = await this.memoClient.send(new ListSecretsCommand({}))
|
||||||
|
return response.SecretList || []
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
const secrets = await this.getSecrets()
|
||||||
|
|
||||||
|
for (const secret of secrets) {
|
||||||
|
if (secret.RotationEnabled) {
|
||||||
|
const now = new Date()
|
||||||
|
const lastRotated = secret.LastRotatedDate ? new Date(secret.LastRotatedDate) : undefined
|
||||||
|
|
||||||
|
if (!lastRotated || now.getTime() - lastRotated.getTime() > 90 * 24 * 60 * 60 * 1000) {
|
||||||
|
nonCompliantResources.push(secret.ARN!)
|
||||||
|
} else {
|
||||||
|
compliantResources.push(secret.ARN!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const arn of nonCompliantResources) {
|
||||||
|
await this.client.send(
|
||||||
|
new RotateSecretCommand({
|
||||||
|
SecretId: arn
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src/bpsets/securityhub/SecurityHubEnabled.ts
Normal file
51
src/bpsets/securityhub/SecurityHubEnabled.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import {
|
||||||
|
SecurityHubClient,
|
||||||
|
DescribeHubCommand,
|
||||||
|
EnableSecurityHubCommand
|
||||||
|
} from '@aws-sdk/client-securityhub'
|
||||||
|
import { STSClient, GetCallerIdentityCommand } from '@aws-sdk/client-sts'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class SecurityHubEnabled implements BPSet {
|
||||||
|
private readonly securityHubClient = new SecurityHubClient({})
|
||||||
|
private readonly stsClient = new STSClient({})
|
||||||
|
private readonly memoSecurityHubClient = Memorizer.memo(this.securityHubClient)
|
||||||
|
private readonly memoStsClient = Memorizer.memo(this.stsClient)
|
||||||
|
|
||||||
|
private readonly getAWSAccountId = async () => {
|
||||||
|
const response = await this.memoStsClient.send(new GetCallerIdentityCommand({}))
|
||||||
|
return response.Account!
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
const awsAccountId = await this.getAWSAccountId()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.memoSecurityHubClient.send(new DescribeHubCommand({}))
|
||||||
|
compliantResources.push(awsAccountId)
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.name === 'InvalidAccessException') {
|
||||||
|
nonCompliantResources.push(awsAccountId)
|
||||||
|
} else {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const accountId of nonCompliantResources) {
|
||||||
|
if (accountId) {
|
||||||
|
await this.securityHubClient.send(new EnableSecurityHubCommand({}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
src/bpsets/sns/SNSEncryptedKMS.ts
Normal file
69
src/bpsets/sns/SNSEncryptedKMS.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import {
|
||||||
|
SNSClient,
|
||||||
|
ListTopicsCommand,
|
||||||
|
GetTopicAttributesCommand,
|
||||||
|
SetTopicAttributesCommand
|
||||||
|
} from '@aws-sdk/client-sns'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class SNSEncryptedKMS implements BPSet {
|
||||||
|
private readonly client = new SNSClient({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
private readonly getTopics = async () => {
|
||||||
|
const topicsResponse = await this.memoClient.send(new ListTopicsCommand({}))
|
||||||
|
const topics = topicsResponse.Topics || []
|
||||||
|
|
||||||
|
const topicDetails = []
|
||||||
|
for (const topic of topics) {
|
||||||
|
const attributes = await this.memoClient.send(
|
||||||
|
new GetTopicAttributesCommand({ TopicArn: topic.TopicArn! })
|
||||||
|
)
|
||||||
|
topicDetails.push({ ...attributes.Attributes, TopicArn: topic.TopicArn! })
|
||||||
|
}
|
||||||
|
|
||||||
|
return topicDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
const topics = await this.getTopics() as any
|
||||||
|
|
||||||
|
for (const topic of topics) {
|
||||||
|
if (topic.KmsMasterKeyId) {
|
||||||
|
compliantResources.push(topic.TopicArn!)
|
||||||
|
} else {
|
||||||
|
nonCompliantResources.push(topic.TopicArn!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: [{ name: 'kms-key-id', value: '<KMS_KEY_ID>' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (
|
||||||
|
nonCompliantResources: string[],
|
||||||
|
requiredParametersForFix: { name: string; value: string }[]
|
||||||
|
) => {
|
||||||
|
const kmsKeyId = requiredParametersForFix.find(param => param.name === 'kms-key-id')?.value
|
||||||
|
|
||||||
|
if (!kmsKeyId) {
|
||||||
|
throw new Error("Required parameter 'kms-key-id' is missing.")
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const arn of nonCompliantResources) {
|
||||||
|
await this.client.send(
|
||||||
|
new SetTopicAttributesCommand({
|
||||||
|
TopicArn: arn,
|
||||||
|
AttributeName: 'KmsMasterKeyId',
|
||||||
|
AttributeValue: kmsKeyId
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
src/bpsets/sns/SNSTopicMessageDeliveryNotificationEnabled.ts
Normal file
79
src/bpsets/sns/SNSTopicMessageDeliveryNotificationEnabled.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import {
|
||||||
|
SNSClient,
|
||||||
|
ListTopicsCommand,
|
||||||
|
GetTopicAttributesCommand,
|
||||||
|
SetTopicAttributesCommand
|
||||||
|
} from '@aws-sdk/client-sns'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class SNSTopicMessageDeliveryNotificationEnabled implements BPSet {
|
||||||
|
private readonly client = new SNSClient({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
private readonly getTopics = async () => {
|
||||||
|
const topicsResponse = await this.memoClient.send(new ListTopicsCommand({}))
|
||||||
|
const topics = topicsResponse.Topics || []
|
||||||
|
|
||||||
|
const topicDetails = []
|
||||||
|
for (const topic of topics) {
|
||||||
|
const attributes = await this.memoClient.send(
|
||||||
|
new GetTopicAttributesCommand({ TopicArn: topic.TopicArn! })
|
||||||
|
)
|
||||||
|
topicDetails.push({ ...attributes.Attributes, TopicArn: topic.TopicArn! })
|
||||||
|
}
|
||||||
|
|
||||||
|
return topicDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
const topics = await this.getTopics()
|
||||||
|
|
||||||
|
for (const topic of topics) {
|
||||||
|
const feedbackRoles = Object.keys(topic).filter(key => key.endsWith('FeedbackRoleArn'))
|
||||||
|
|
||||||
|
if (feedbackRoles.length > 0) {
|
||||||
|
compliantResources.push(topic.TopicArn!)
|
||||||
|
} else {
|
||||||
|
nonCompliantResources.push(topic.TopicArn!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: [
|
||||||
|
{ name: 'sns-feedback-role-arn', value: '<FEEDBACK_ROLE_ARN>' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (
|
||||||
|
nonCompliantResources: string[],
|
||||||
|
requiredParametersForFix: { name: string; value: string }[]
|
||||||
|
) => {
|
||||||
|
const feedbackRoleArn = requiredParametersForFix.find(
|
||||||
|
param => param.name === 'sns-feedback-role-arn'
|
||||||
|
)?.value
|
||||||
|
|
||||||
|
if (!feedbackRoleArn) {
|
||||||
|
throw new Error("Required parameter 'sns-feedback-role-arn' is missing.")
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const arn of nonCompliantResources) {
|
||||||
|
await this.client.send(
|
||||||
|
new SetTopicAttributesCommand({
|
||||||
|
TopicArn: arn,
|
||||||
|
AttributeName: 'DeliveryPolicy',
|
||||||
|
AttributeValue: JSON.stringify({
|
||||||
|
http: {
|
||||||
|
DefaultFeedbackRoleArn: feedbackRoleArn
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
src/bpsets/vpc/EC2TransitGatewayAutoVPCAttachDisabled.ts
Normal file
49
src/bpsets/vpc/EC2TransitGatewayAutoVPCAttachDisabled.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
EC2Client,
|
||||||
|
DescribeTransitGatewaysCommand,
|
||||||
|
ModifyTransitGatewayCommand
|
||||||
|
} from '@aws-sdk/client-ec2'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class EC2TransitGatewayAutoVPCAttachDisabled implements BPSet {
|
||||||
|
private readonly client = new EC2Client({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
|
||||||
|
const response = await this.memoClient.send(new DescribeTransitGatewaysCommand({}))
|
||||||
|
const transitGateways = response.TransitGateways || []
|
||||||
|
|
||||||
|
for (const gateway of transitGateways) {
|
||||||
|
if (gateway.Options?.AutoAcceptSharedAttachments === 'enable') {
|
||||||
|
nonCompliantResources.push(gateway.TransitGatewayArn!)
|
||||||
|
} else {
|
||||||
|
compliantResources.push(gateway.TransitGatewayArn!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const arn of nonCompliantResources) {
|
||||||
|
const transitGatewayId = arn.split(':transit-gateway/')[1]
|
||||||
|
|
||||||
|
await this.client.send(
|
||||||
|
new ModifyTransitGatewayCommand({
|
||||||
|
TransitGatewayId: transitGatewayId,
|
||||||
|
Options: {
|
||||||
|
AutoAcceptSharedAttachments: 'disable'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
src/bpsets/vpc/RestrictedCommonPorts.ts
Normal file
57
src/bpsets/vpc/RestrictedCommonPorts.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import {
|
||||||
|
EC2Client,
|
||||||
|
DescribeSecurityGroupRulesCommand,
|
||||||
|
RevokeSecurityGroupIngressCommand
|
||||||
|
} from '@aws-sdk/client-ec2'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class RestrictedCommonPorts implements BPSet {
|
||||||
|
private readonly client = new EC2Client({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
private readonly getSecurityGroupRules = async () => {
|
||||||
|
const response = await this.memoClient.send(new DescribeSecurityGroupRulesCommand({}))
|
||||||
|
return response.SecurityGroupRules || []
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
|
||||||
|
const commonPorts = [-1, 22, 80, 3306, 3389, 5432, 6379, 11211]
|
||||||
|
const rules = await this.getSecurityGroupRules()
|
||||||
|
|
||||||
|
for (const rule of rules) {
|
||||||
|
if (
|
||||||
|
!rule.IsEgress &&
|
||||||
|
commonPorts.includes(rule.FromPort!) &&
|
||||||
|
commonPorts.includes(rule.ToPort!) &&
|
||||||
|
!rule.PrefixListId
|
||||||
|
) {
|
||||||
|
nonCompliantResources.push(`${rule.GroupId} / ${rule.SecurityGroupRuleId}`)
|
||||||
|
} else {
|
||||||
|
compliantResources.push(`${rule.GroupId} / ${rule.SecurityGroupRuleId}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const resource of nonCompliantResources) {
|
||||||
|
const [groupId, ruleId] = resource.split(' / ')
|
||||||
|
|
||||||
|
await this.client.send(
|
||||||
|
new RevokeSecurityGroupIngressCommand({
|
||||||
|
GroupId: groupId,
|
||||||
|
SecurityGroupRuleIds: [ruleId]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
src/bpsets/vpc/RestrictedSSH.ts
Normal file
55
src/bpsets/vpc/RestrictedSSH.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import {
|
||||||
|
EC2Client,
|
||||||
|
DescribeSecurityGroupRulesCommand,
|
||||||
|
RevokeSecurityGroupIngressCommand
|
||||||
|
} from '@aws-sdk/client-ec2'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class RestrictedSSH implements BPSet {
|
||||||
|
private readonly client = new EC2Client({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
private readonly getSecurityGroupRules = async () => {
|
||||||
|
const response = await this.memoClient.send(new DescribeSecurityGroupRulesCommand({}))
|
||||||
|
return response.SecurityGroupRules || []
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
|
||||||
|
const rules = await this.getSecurityGroupRules()
|
||||||
|
for (const rule of rules) {
|
||||||
|
if (
|
||||||
|
!rule.IsEgress &&
|
||||||
|
rule.FromPort! <= 22 &&
|
||||||
|
rule.ToPort! >= 22 &&
|
||||||
|
rule.CidrIpv4 === '0.0.0.0/0'
|
||||||
|
) {
|
||||||
|
nonCompliantResources.push(`${rule.GroupId} / ${rule.SecurityGroupRuleId}`)
|
||||||
|
} else {
|
||||||
|
compliantResources.push(`${rule.GroupId} / ${rule.SecurityGroupRuleId}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const resource of nonCompliantResources) {
|
||||||
|
const [groupId, ruleId] = resource.split(' / ')
|
||||||
|
|
||||||
|
await this.client.send(
|
||||||
|
new RevokeSecurityGroupIngressCommand({
|
||||||
|
GroupId: groupId,
|
||||||
|
SecurityGroupRuleIds: [ruleId]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
src/bpsets/vpc/SubnetAutoAssignPublicIPDisabled.ts
Normal file
45
src/bpsets/vpc/SubnetAutoAssignPublicIPDisabled.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import {
|
||||||
|
EC2Client,
|
||||||
|
DescribeSubnetsCommand,
|
||||||
|
ModifySubnetAttributeCommand
|
||||||
|
} from '@aws-sdk/client-ec2'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class SubnetAutoAssignPublicIPDisabled implements BPSet {
|
||||||
|
private readonly client = new EC2Client({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
|
||||||
|
const response = await this.memoClient.send(new DescribeSubnetsCommand({}))
|
||||||
|
const subnets = response.Subnets || []
|
||||||
|
|
||||||
|
for (const subnet of subnets) {
|
||||||
|
if (subnet.MapPublicIpOnLaunch) {
|
||||||
|
nonCompliantResources.push(subnet.SubnetId!)
|
||||||
|
} else {
|
||||||
|
compliantResources.push(subnet.SubnetId!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const subnetId of nonCompliantResources) {
|
||||||
|
await this.client.send(
|
||||||
|
new ModifySubnetAttributeCommand({
|
||||||
|
SubnetId: subnetId,
|
||||||
|
MapPublicIpOnLaunch: { Value: false }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
src/bpsets/vpc/VPCDefaultSecurityGroupClosed.ts
Normal file
56
src/bpsets/vpc/VPCDefaultSecurityGroupClosed.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
EC2Client,
|
||||||
|
DescribeSecurityGroupsCommand,
|
||||||
|
RevokeSecurityGroupIngressCommand,
|
||||||
|
RevokeSecurityGroupEgressCommand
|
||||||
|
} from '@aws-sdk/client-ec2'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class VPCDefaultSecurityGroupClosed implements BPSet {
|
||||||
|
private readonly client = new EC2Client({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
|
||||||
|
const response = await this.memoClient.send(
|
||||||
|
new DescribeSecurityGroupsCommand({
|
||||||
|
Filters: [{ Name: 'group-name', Values: ['default'] }]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const securityGroups = response.SecurityGroups || []
|
||||||
|
|
||||||
|
for (const group of securityGroups) {
|
||||||
|
if (group.IpPermissions?.length || group.IpPermissionsEgress?.length) {
|
||||||
|
nonCompliantResources.push(group.GroupId!)
|
||||||
|
} else {
|
||||||
|
compliantResources.push(group.GroupId!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const groupId of nonCompliantResources) {
|
||||||
|
await this.client.send(
|
||||||
|
new RevokeSecurityGroupIngressCommand({
|
||||||
|
GroupId: groupId,
|
||||||
|
IpPermissions: []
|
||||||
|
})
|
||||||
|
)
|
||||||
|
await this.client.send(
|
||||||
|
new RevokeSecurityGroupEgressCommand({
|
||||||
|
GroupId: groupId,
|
||||||
|
IpPermissions: []
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
src/bpsets/vpc/VPCFlowLogsEnabled.ts
Normal file
66
src/bpsets/vpc/VPCFlowLogsEnabled.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import {
|
||||||
|
EC2Client,
|
||||||
|
DescribeVpcsCommand,
|
||||||
|
DescribeFlowLogsCommand,
|
||||||
|
CreateFlowLogsCommand
|
||||||
|
} from '@aws-sdk/client-ec2'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class VPCFlowLogsEnabled implements BPSet {
|
||||||
|
private readonly client = new EC2Client({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
|
||||||
|
const flowLogsResponse = await this.memoClient.send(new DescribeFlowLogsCommand({}))
|
||||||
|
const flowLogs = flowLogsResponse.FlowLogs || []
|
||||||
|
const flowLogEnabledVpcs = flowLogs.map(log => log.ResourceId!)
|
||||||
|
|
||||||
|
const vpcsResponse = await this.memoClient.send(new DescribeVpcsCommand({}))
|
||||||
|
const vpcs = vpcsResponse.Vpcs || []
|
||||||
|
|
||||||
|
for (const vpc of vpcs) {
|
||||||
|
if (flowLogEnabledVpcs.includes(vpc.VpcId!)) {
|
||||||
|
compliantResources.push(vpc.VpcId!)
|
||||||
|
} else {
|
||||||
|
nonCompliantResources.push(vpc.VpcId!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: [
|
||||||
|
{ name: 'log-group-name', value: '<LOG_GROUP_NAME>' },
|
||||||
|
{ name: 'iam-role-arn', value: '<IAM_ROLE_ARN>' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (
|
||||||
|
nonCompliantResources: string[],
|
||||||
|
requiredParametersForFix: { name: string; value: string }[]
|
||||||
|
) => {
|
||||||
|
const logGroupName = requiredParametersForFix.find(param => param.name === 'log-group-name')?.value
|
||||||
|
const iamRoleArn = requiredParametersForFix.find(param => param.name === 'iam-role-arn')?.value
|
||||||
|
|
||||||
|
if (!logGroupName || !iamRoleArn) {
|
||||||
|
throw new Error("Required parameters 'log-group-name' and 'iam-role-arn' are missing.")
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const vpcId of nonCompliantResources) {
|
||||||
|
await this.client.send(
|
||||||
|
new CreateFlowLogsCommand({
|
||||||
|
ResourceIds: [vpcId],
|
||||||
|
ResourceType: 'VPC',
|
||||||
|
LogGroupName: logGroupName,
|
||||||
|
DeliverLogsPermissionArn: iamRoleArn,
|
||||||
|
TrafficType: 'ALL'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
src/bpsets/vpc/VPCNetworkACLUnusedCheck.ts
Normal file
44
src/bpsets/vpc/VPCNetworkACLUnusedCheck.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
EC2Client,
|
||||||
|
DescribeNetworkAclsCommand,
|
||||||
|
DeleteNetworkAclCommand
|
||||||
|
} from '@aws-sdk/client-ec2'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class VPCNetworkACLUnusedCheck implements BPSet {
|
||||||
|
private readonly client = new EC2Client({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
|
||||||
|
const response = await this.memoClient.send(new DescribeNetworkAclsCommand({}))
|
||||||
|
const networkAcls = response.NetworkAcls || []
|
||||||
|
|
||||||
|
for (const acl of networkAcls) {
|
||||||
|
if (!acl.Associations || acl.Associations.length === 0) {
|
||||||
|
nonCompliantResources.push(acl.NetworkAclId!)
|
||||||
|
} else {
|
||||||
|
compliantResources.push(acl.NetworkAclId!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const aclId of nonCompliantResources) {
|
||||||
|
await this.client.send(
|
||||||
|
new DeleteNetworkAclCommand({
|
||||||
|
NetworkAclId: aclId
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
src/bpsets/vpc/VPCPeeringDNSResolutionCheck.ts
Normal file
56
src/bpsets/vpc/VPCPeeringDNSResolutionCheck.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
EC2Client,
|
||||||
|
DescribeVpcPeeringConnectionsCommand,
|
||||||
|
ModifyVpcPeeringConnectionOptionsCommand
|
||||||
|
} from '@aws-sdk/client-ec2'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class VPCPeeringDNSResolutionCheck implements BPSet {
|
||||||
|
private readonly client = new EC2Client({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
|
||||||
|
const response = await this.memoClient.send(new DescribeVpcPeeringConnectionsCommand({}))
|
||||||
|
const vpcPeeringConnections = response.VpcPeeringConnections || []
|
||||||
|
|
||||||
|
for (const connection of vpcPeeringConnections) {
|
||||||
|
const accepterOptions = connection.AccepterVpcInfo?.PeeringOptions
|
||||||
|
const requesterOptions = connection.RequesterVpcInfo?.PeeringOptions
|
||||||
|
|
||||||
|
if (
|
||||||
|
!accepterOptions?.AllowDnsResolutionFromRemoteVpc ||
|
||||||
|
!requesterOptions?.AllowDnsResolutionFromRemoteVpc
|
||||||
|
) {
|
||||||
|
nonCompliantResources.push(connection.VpcPeeringConnectionId!)
|
||||||
|
} else {
|
||||||
|
compliantResources.push(connection.VpcPeeringConnectionId!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const connectionId of nonCompliantResources) {
|
||||||
|
await this.client.send(
|
||||||
|
new ModifyVpcPeeringConnectionOptionsCommand({
|
||||||
|
VpcPeeringConnectionId: connectionId,
|
||||||
|
AccepterPeeringConnectionOptions: {
|
||||||
|
AllowDnsResolutionFromRemoteVpc: true
|
||||||
|
},
|
||||||
|
RequesterPeeringConnectionOptions: {
|
||||||
|
AllowDnsResolutionFromRemoteVpc: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
src/bpsets/vpc/VPCSGOpenOnlyToAuthorizedPorts.ts
Normal file
56
src/bpsets/vpc/VPCSGOpenOnlyToAuthorizedPorts.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
EC2Client,
|
||||||
|
DescribeSecurityGroupRulesCommand,
|
||||||
|
RevokeSecurityGroupIngressCommand
|
||||||
|
} from '@aws-sdk/client-ec2'
|
||||||
|
import { BPSet } from '../BPSet'
|
||||||
|
import { Memorizer } from '../../Memorizer'
|
||||||
|
|
||||||
|
export class VPCSGOpenOnlyToAuthorizedPorts implements BPSet {
|
||||||
|
private readonly client = new EC2Client({})
|
||||||
|
private readonly memoClient = Memorizer.memo(this.client)
|
||||||
|
|
||||||
|
private readonly getSecurityGroupRules = async () => {
|
||||||
|
const response = await this.memoClient.send(new DescribeSecurityGroupRulesCommand({}))
|
||||||
|
return response.SecurityGroupRules || []
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = []
|
||||||
|
const nonCompliantResources: string[] = []
|
||||||
|
const authorizedPorts = [80, 443] // Example authorized ports
|
||||||
|
|
||||||
|
const rules = await this.getSecurityGroupRules()
|
||||||
|
for (const rule of rules) {
|
||||||
|
if (
|
||||||
|
!rule.IsEgress &&
|
||||||
|
(rule.CidrIpv4 === '0.0.0.0/0' || rule.CidrIpv6 === '::/0') &&
|
||||||
|
!authorizedPorts.includes(rule.FromPort!) &&
|
||||||
|
!authorizedPorts.includes(rule.ToPort!)
|
||||||
|
) {
|
||||||
|
nonCompliantResources.push(`${rule.GroupId} / ${rule.SecurityGroupRuleId}`)
|
||||||
|
} else {
|
||||||
|
compliantResources.push(`${rule.GroupId} / ${rule.SecurityGroupRuleId}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const resource of nonCompliantResources) {
|
||||||
|
const [groupId, ruleId] = resource.split(' / ')
|
||||||
|
|
||||||
|
await this.client.send(
|
||||||
|
new RevokeSecurityGroupIngressCommand({
|
||||||
|
GroupId: groupId,
|
||||||
|
SecurityGroupRuleIds: [ruleId]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
src/bpsets/waf/WAFv2LoggingEnabled.ts
Normal file
74
src/bpsets/waf/WAFv2LoggingEnabled.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import {
|
||||||
|
WAFV2Client,
|
||||||
|
ListWebACLsCommand,
|
||||||
|
GetLoggingConfigurationCommand,
|
||||||
|
PutLoggingConfigurationCommand,
|
||||||
|
} from '@aws-sdk/client-wafv2';
|
||||||
|
import { BPSet } from '../BPSet';
|
||||||
|
import { Memorizer } from '../../Memorizer';
|
||||||
|
|
||||||
|
export class WAFv2LoggingEnabled implements BPSet {
|
||||||
|
private readonly regionalClient = new WAFV2Client({});
|
||||||
|
private readonly globalClient = new WAFV2Client({ region: 'us-east-1' });
|
||||||
|
private readonly memoRegionalClient = Memorizer.memo(this.regionalClient);
|
||||||
|
private readonly memoGlobalClient = Memorizer.memo(this.globalClient);
|
||||||
|
|
||||||
|
private readonly getWebACLs = async (scope: 'REGIONAL' | 'CLOUDFRONT') => {
|
||||||
|
const client = scope === 'REGIONAL' ? this.memoRegionalClient : this.memoGlobalClient;
|
||||||
|
const response = await client.send(new ListWebACLsCommand({ Scope: scope }));
|
||||||
|
return response.WebACLs || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = [];
|
||||||
|
const nonCompliantResources: string[] = [];
|
||||||
|
|
||||||
|
for (const scope of ['REGIONAL', 'CLOUDFRONT'] as const) {
|
||||||
|
const client = scope === 'REGIONAL' ? this.memoRegionalClient : this.memoGlobalClient;
|
||||||
|
const webACLs = await this.getWebACLs(scope);
|
||||||
|
|
||||||
|
for (const webACL of webACLs) {
|
||||||
|
try {
|
||||||
|
await client.send(new GetLoggingConfigurationCommand({ ResourceArn: webACL.ARN }));
|
||||||
|
compliantResources.push(webACL.ARN!);
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.name === 'WAFNonexistentItemException') {
|
||||||
|
nonCompliantResources.push(webACL.ARN!);
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: [{ name: 'log-group-arn', value: '<LOG_GROUP_ARN>' }],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
public readonly fix = async (
|
||||||
|
nonCompliantResources: string[],
|
||||||
|
requiredParametersForFix: { name: string; value: string }[]
|
||||||
|
) => {
|
||||||
|
const logGroupArn = requiredParametersForFix.find(param => param.name === 'log-group-arn')?.value;
|
||||||
|
|
||||||
|
if (!logGroupArn) {
|
||||||
|
throw new Error("Required parameter 'log-group-arn' is missing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const arn of nonCompliantResources) {
|
||||||
|
const client = arn.includes('global') ? this.globalClient : this.regionalClient;
|
||||||
|
|
||||||
|
await client.send(
|
||||||
|
new PutLoggingConfigurationCommand({
|
||||||
|
LoggingConfiguration: {
|
||||||
|
ResourceArn: arn,
|
||||||
|
LogDestinationConfigs: [logGroupArn],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
71
src/bpsets/waf/WAFv2RuleGroupLoggingEnabled.ts
Normal file
71
src/bpsets/waf/WAFv2RuleGroupLoggingEnabled.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import {
|
||||||
|
WAFV2Client,
|
||||||
|
ListRuleGroupsCommand,
|
||||||
|
GetRuleGroupCommand,
|
||||||
|
UpdateRuleGroupCommand,
|
||||||
|
} from '@aws-sdk/client-wafv2';
|
||||||
|
import { BPSet } from '../BPSet';
|
||||||
|
import { Memorizer } from '../../Memorizer';
|
||||||
|
|
||||||
|
export class WAFv2RuleGroupLoggingEnabled implements BPSet {
|
||||||
|
private readonly regionalClient = new WAFV2Client({});
|
||||||
|
private readonly globalClient = new WAFV2Client({ region: 'us-east-1' });
|
||||||
|
private readonly memoRegionalClient = Memorizer.memo(this.regionalClient);
|
||||||
|
private readonly memoGlobalClient = Memorizer.memo(this.globalClient);
|
||||||
|
|
||||||
|
private readonly getRuleGroups = async (scope: 'REGIONAL' | 'CLOUDFRONT') => {
|
||||||
|
const client = scope === 'REGIONAL' ? this.memoRegionalClient : this.memoGlobalClient;
|
||||||
|
const response = await client.send(new ListRuleGroupsCommand({ Scope: scope }));
|
||||||
|
return response.RuleGroups || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = [];
|
||||||
|
const nonCompliantResources: string[] = [];
|
||||||
|
|
||||||
|
for (const scope of ['REGIONAL', 'CLOUDFRONT'] as const) {
|
||||||
|
const client = scope === 'REGIONAL' ? this.memoRegionalClient : this.memoGlobalClient;
|
||||||
|
const ruleGroups = await this.getRuleGroups(scope);
|
||||||
|
|
||||||
|
for (const ruleGroup of ruleGroups) {
|
||||||
|
const details = await client.send(
|
||||||
|
new GetRuleGroupCommand({ Name: ruleGroup.Name!, Id: ruleGroup.Id!, Scope: scope })
|
||||||
|
);
|
||||||
|
|
||||||
|
if (details.RuleGroup?.VisibilityConfig?.CloudWatchMetricsEnabled) {
|
||||||
|
compliantResources.push(ruleGroup.ARN!);
|
||||||
|
} else {
|
||||||
|
nonCompliantResources.push(ruleGroup.ARN!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: [],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
public readonly fix = async (nonCompliantResources: string[]) => {
|
||||||
|
for (const arn of nonCompliantResources) {
|
||||||
|
const client = arn.includes('global') ? this.globalClient : this.regionalClient;
|
||||||
|
|
||||||
|
const [name, id] = arn.split('/')[1].split(':');
|
||||||
|
|
||||||
|
await client.send(
|
||||||
|
new UpdateRuleGroupCommand({
|
||||||
|
Name: name,
|
||||||
|
Id: id,
|
||||||
|
Scope: arn.includes('global') ? 'CLOUDFRONT' : 'REGIONAL',
|
||||||
|
VisibilityConfig: {
|
||||||
|
CloudWatchMetricsEnabled: true,
|
||||||
|
MetricName: `WAFRuleGroup-${name}`,
|
||||||
|
SampledRequestsEnabled: true,
|
||||||
|
},
|
||||||
|
LockToken: undefined
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
93
src/bpsets/waf/WAFv2RuleGroupNotEmpty.ts
Normal file
93
src/bpsets/waf/WAFv2RuleGroupNotEmpty.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import {
|
||||||
|
WAFV2Client,
|
||||||
|
ListRuleGroupsCommand,
|
||||||
|
GetRuleGroupCommand,
|
||||||
|
UpdateRuleGroupCommand
|
||||||
|
} from '@aws-sdk/client-wafv2';
|
||||||
|
import { BPSet } from '../BPSet';
|
||||||
|
import { Memorizer } from '../../Memorizer';
|
||||||
|
|
||||||
|
export class WAFv2RuleGroupNotEmpty implements BPSet {
|
||||||
|
private readonly regionalClient = new WAFV2Client({});
|
||||||
|
private readonly globalClient = new WAFV2Client({ region: 'us-east-1' });
|
||||||
|
private readonly memoRegionalClient = Memorizer.memo(this.regionalClient);
|
||||||
|
private readonly memoGlobalClient = Memorizer.memo(this.globalClient);
|
||||||
|
|
||||||
|
private readonly getRuleGroups = async (scope: 'REGIONAL' | 'CLOUDFRONT') => {
|
||||||
|
const client = scope === 'REGIONAL' ? this.memoRegionalClient : this.memoGlobalClient;
|
||||||
|
const response = await client.send(new ListRuleGroupsCommand({ Scope: scope }));
|
||||||
|
return response.RuleGroups || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = [];
|
||||||
|
const nonCompliantResources: string[] = [];
|
||||||
|
|
||||||
|
for (const scope of ['REGIONAL', 'CLOUDFRONT'] as const) {
|
||||||
|
const client = scope === 'REGIONAL' ? this.memoRegionalClient : this.memoGlobalClient;
|
||||||
|
const ruleGroups = await this.getRuleGroups(scope);
|
||||||
|
|
||||||
|
for (const ruleGroup of ruleGroups) {
|
||||||
|
const details = await client.send(
|
||||||
|
new GetRuleGroupCommand({ Name: ruleGroup.Name!, Id: ruleGroup.Id!, Scope: scope })
|
||||||
|
);
|
||||||
|
|
||||||
|
if (details.RuleGroup?.Rules?.length! > 0) {
|
||||||
|
compliantResources.push(ruleGroup.ARN!);
|
||||||
|
} else {
|
||||||
|
nonCompliantResources.push(ruleGroup.ARN!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: [{ name: 'default-rule', value: '<DEFAULT_RULE>' }]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
public readonly fix = async (
|
||||||
|
nonCompliantResources: string[],
|
||||||
|
requiredParametersForFix: { name: string; value: string }[]
|
||||||
|
) => {
|
||||||
|
const defaultRule = requiredParametersForFix.find(param => param.name === 'default-rule')?.value;
|
||||||
|
|
||||||
|
if (!defaultRule) {
|
||||||
|
throw new Error("Required parameter 'default-rule' is missing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const arn of nonCompliantResources) {
|
||||||
|
const client = arn.includes('global') ? this.globalClient : this.regionalClient;
|
||||||
|
|
||||||
|
const [name, id] = arn.split('/')[1].split(':');
|
||||||
|
|
||||||
|
await client.send(
|
||||||
|
new UpdateRuleGroupCommand({
|
||||||
|
Name: name,
|
||||||
|
Id: id,
|
||||||
|
Scope: arn.includes('global') ? 'CLOUDFRONT' : 'REGIONAL',
|
||||||
|
LockToken: undefined,
|
||||||
|
Rules: [
|
||||||
|
{
|
||||||
|
Name: 'DefaultRule',
|
||||||
|
Priority: 1,
|
||||||
|
Action: { Allow: {} },
|
||||||
|
Statement: JSON.parse(defaultRule),
|
||||||
|
VisibilityConfig: {
|
||||||
|
CloudWatchMetricsEnabled: true,
|
||||||
|
MetricName: `DefaultRule-${name}`,
|
||||||
|
SampledRequestsEnabled: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
VisibilityConfig: {
|
||||||
|
CloudWatchMetricsEnabled: true,
|
||||||
|
MetricName: `RuleGroup-${name}`,
|
||||||
|
SampledRequestsEnabled: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
94
src/bpsets/waf/WAFv2WebACLNotEmpty.ts
Normal file
94
src/bpsets/waf/WAFv2WebACLNotEmpty.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import {
|
||||||
|
WAFV2Client,
|
||||||
|
ListWebACLsCommand,
|
||||||
|
GetWebACLCommand,
|
||||||
|
UpdateWebACLCommand
|
||||||
|
} from '@aws-sdk/client-wafv2';
|
||||||
|
import { BPSet } from '../BPSet';
|
||||||
|
import { Memorizer } from '../../Memorizer';
|
||||||
|
|
||||||
|
export class WAFv2WebACLNotEmpty implements BPSet {
|
||||||
|
private readonly regionalClient = new WAFV2Client({});
|
||||||
|
private readonly globalClient = new WAFV2Client({ region: 'us-east-1' });
|
||||||
|
private readonly memoRegionalClient = Memorizer.memo(this.regionalClient);
|
||||||
|
private readonly memoGlobalClient = Memorizer.memo(this.globalClient);
|
||||||
|
|
||||||
|
private readonly getWebACLs = async (scope: 'REGIONAL' | 'CLOUDFRONT') => {
|
||||||
|
const client = scope === 'REGIONAL' ? this.memoRegionalClient : this.memoGlobalClient;
|
||||||
|
const response = await client.send(new ListWebACLsCommand({ Scope: scope }));
|
||||||
|
return response.WebACLs || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
public readonly check = async () => {
|
||||||
|
const compliantResources: string[] = [];
|
||||||
|
const nonCompliantResources: string[] = [];
|
||||||
|
|
||||||
|
for (const scope of ['REGIONAL', 'CLOUDFRONT'] as const) {
|
||||||
|
const client = scope === 'REGIONAL' ? this.memoRegionalClient : this.memoGlobalClient;
|
||||||
|
const webACLs = await this.getWebACLs(scope);
|
||||||
|
|
||||||
|
for (const webACL of webACLs) {
|
||||||
|
const details = await client.send(
|
||||||
|
new GetWebACLCommand({ Name: webACL.Name!, Id: webACL.Id!, Scope: scope })
|
||||||
|
);
|
||||||
|
|
||||||
|
if (details.WebACL?.Rules?.length! > 0) {
|
||||||
|
compliantResources.push(webACL.ARN!);
|
||||||
|
} else {
|
||||||
|
nonCompliantResources.push(webACL.ARN!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
compliantResources,
|
||||||
|
nonCompliantResources,
|
||||||
|
requiredParametersForFix: [{ name: 'default-rule', value: '<DEFAULT_RULE>' }]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
public readonly fix = async (
|
||||||
|
nonCompliantResources: string[],
|
||||||
|
requiredParametersForFix: { name: string; value: string }[]
|
||||||
|
) => {
|
||||||
|
const defaultRule = requiredParametersForFix.find(param => param.name === 'default-rule')?.value;
|
||||||
|
|
||||||
|
if (!defaultRule) {
|
||||||
|
throw new Error("Required parameter 'default-rule' is missing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const arn of nonCompliantResources) {
|
||||||
|
const client = arn.includes('global') ? this.globalClient : this.regionalClient;
|
||||||
|
|
||||||
|
const [name, id] = arn.split('/')[1].split(':');
|
||||||
|
|
||||||
|
await client.send(
|
||||||
|
new UpdateWebACLCommand({
|
||||||
|
Name: name,
|
||||||
|
Id: id,
|
||||||
|
Scope: arn.includes('global') ? 'CLOUDFRONT' : 'REGIONAL',
|
||||||
|
LockToken: undefined,
|
||||||
|
Rules: [
|
||||||
|
{
|
||||||
|
Name: 'DefaultRule',
|
||||||
|
Priority: 1,
|
||||||
|
Action: { Allow: {} },
|
||||||
|
Statement: JSON.parse(defaultRule),
|
||||||
|
VisibilityConfig: {
|
||||||
|
CloudWatchMetricsEnabled: true,
|
||||||
|
MetricName: `DefaultRule-${name}`,
|
||||||
|
SampledRequestsEnabled: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
DefaultAction: { Allow: {} },
|
||||||
|
VisibilityConfig: {
|
||||||
|
CloudWatchMetricsEnabled: true,
|
||||||
|
MetricName: `WebACL-${name}`,
|
||||||
|
SampledRequestsEnabled: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
Reference in New Issue
Block a user