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-s3": "^3.717.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-sts": "^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':
|
||||
specifier: ^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':
|
||||
specifier: ^3.716.0
|
||||
version: 3.716.0
|
||||
@ -222,6 +231,18 @@ packages:
|
||||
resolution: {integrity: sha512-jzaH8IskAXVnqlZ3/H/ROwrB2HCnq/atlN7Hi7FIfjWvMPf5nfcJKfzJ1MXFX0EQR5qO6X4TbK7rgi7Bjw9NjQ==}
|
||||
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':
|
||||
resolution: {integrity: sha512-da2wTUBCLGRoQf5Ahm/LuUIR/OkQ09kaX7yYRC2Vw+TOcMXbozJSzXbm99SXsOL4u8a8PRq+Vwfptc36e18Feg==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
@ -2237,6 +2258,146 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- 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':
|
||||
dependencies:
|
||||
'@aws-crypto/sha256-browser': 5.2.0
|
||||
@ -2703,7 +2864,7 @@ snapshots:
|
||||
'@aws-sdk/core': 3.716.0
|
||||
'@aws-sdk/types': 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/types': 3.7.2
|
||||
tslib: 2.8.1
|
||||
|
@ -2,7 +2,15 @@ export interface BPSet {
|
||||
check: () => Promise<{
|
||||
compliantResources: 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