Skip to content

Commit c4ee3e8

Browse files
committed
CCM-8574: add letter file validation
1 parent afc4890 commit c4ee3e8

File tree

62 files changed

+8304
-683
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+8304
-683
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ node_modules
3333

3434
# production
3535
/build
36+
dist
3637

3738
# misc
3839
.DS_Store

infrastructure/terraform/modules/backend-api/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,18 @@ No requirements.
3030
| <a name="module_authorizer_lambda"></a> [authorizer\_lambda](#module\_authorizer\_lambda) | ../lambda-function | n/a |
3131
| <a name="module_build_template_client"></a> [build\_template\_client](#module\_build\_template\_client) | ../typescript-build-zip | n/a |
3232
| <a name="module_build_template_lambda"></a> [build\_template\_lambda](#module\_build\_template\_lambda) | ../typescript-build-zip | n/a |
33-
| <a name="module_build_virus_scan_lambdas"></a> [build\_virus\_scan\_lambdas](#module\_build\_virus\_scan\_lambdas) | ../typescript-build-zip | n/a |
3433
| <a name="module_create_letter_template_lambda"></a> [create\_letter\_template\_lambda](#module\_create\_letter\_template\_lambda) | ../lambda-function | n/a |
3534
| <a name="module_create_template_lambda"></a> [create\_template\_lambda](#module\_create\_template\_lambda) | ../lambda-function | n/a |
3635
| <a name="module_get_template_lambda"></a> [get\_template\_lambda](#module\_get\_template\_lambda) | ../lambda-function | n/a |
3736
| <a name="module_lambda_copy_scanned_object_to_internal"></a> [lambda\_copy\_scanned\_object\_to\_internal](#module\_lambda\_copy\_scanned\_object\_to\_internal) | ../lambda-function | n/a |
3837
| <a name="module_lambda_delete_failed_scanned_object"></a> [lambda\_delete\_failed\_scanned\_object](#module\_lambda\_delete\_failed\_scanned\_object) | ../lambda-function | n/a |
39-
| <a name="module_lambda_enrich_guardduty_scan_result"></a> [lambda\_enrich\_guardduty\_scan\_result](#module\_lambda\_enrich\_guardduty\_scan\_result) | ../lambda-function | n/a |
38+
| <a name="module_lambda_layer_pdfjs"></a> [lambda\_layer\_pdfjs](#module\_lambda\_layer\_pdfjs) | ../lambda_layer | n/a |
4039
| <a name="module_lambda_set_file_virus_scan_status"></a> [lambda\_set\_file\_virus\_scan\_status](#module\_lambda\_set\_file\_virus\_scan\_status) | ../lambda-function | n/a |
40+
| <a name="module_lambda_validate_letter_template_files"></a> [lambda\_validate\_letter\_template\_files](#module\_lambda\_validate\_letter\_template\_files) | ../lambda-function | n/a |
4141
| <a name="module_list_template_lambda"></a> [list\_template\_lambda](#module\_list\_template\_lambda) | ../lambda-function | n/a |
4242
| <a name="module_s3bucket_internal"></a> [s3bucket\_internal](#module\_s3bucket\_internal) | git::https://github.com/NHSDigital/nhs-notify-shared-modules.git//infrastructure/modules/s3bucket | v1.0.8 |
4343
| <a name="module_s3bucket_quarantine"></a> [s3bucket\_quarantine](#module\_s3bucket\_quarantine) | git::https://github.com/NHSDigital/nhs-notify-shared-modules.git//infrastructure/modules/s3bucket | v1.0.8 |
44-
| <a name="module_sqs_quarantine_scan_enrichment"></a> [sqs\_quarantine\_scan\_enrichment](#module\_sqs\_quarantine\_scan\_enrichment) | git::https://github.com/NHSDigital/nhs-notify-shared-modules.git//infrastructure/modules/sqs | v1.0.8 |
44+
| <a name="module_sqs_validate_letter_template_files_dlq"></a> [sqs\_validate\_letter\_template\_files\_dlq](#module\_sqs\_validate\_letter\_template\_files\_dlq) | git::https://github.com/NHSDigital/nhs-notify-shared-modules.git//infrastructure/modules/sqs | v1.0.8 |
4545
| <a name="module_sqs_virus_scan_failed_delete_object_dlq"></a> [sqs\_virus\_scan\_failed\_delete\_object\_dlq](#module\_sqs\_virus\_scan\_failed\_delete\_object\_dlq) | git::https://github.com/NHSDigital/nhs-notify-shared-modules.git//infrastructure/modules/sqs | v1.0.8 |
4646
| <a name="module_sqs_virus_scan_passed_copy_object_dlq"></a> [sqs\_virus\_scan\_passed\_copy\_object\_dlq](#module\_sqs\_virus\_scan\_passed\_copy\_object\_dlq) | git::https://github.com/NHSDigital/nhs-notify-shared-modules.git//infrastructure/modules/sqs | v1.0.8 |
4747
| <a name="module_sqs_virus_scan_set_file_status_dlq"></a> [sqs\_virus\_scan\_set\_file\_status\_dlq](#module\_sqs\_virus\_scan\_set\_file\_status\_dlq) | git::https://github.com/NHSDigital/nhs-notify-shared-modules.git//infrastructure/modules/sqs | v1.0.8 |
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
resource "aws_cloudwatch_event_rule" "guardduty_quarantine_scan_no_threats" {
2+
name = "${local.csi}-quarantine-scan-no_threats"
3+
description = "Matches quarantine 'GuardDuty Malware Protection Object Scan Result' events where the scan result is NO_THREATS_FOUND"
4+
5+
event_pattern = jsonencode({
6+
source = ["aws.guardduty"]
7+
detail-type = ["GuardDuty Malware Protection Object Scan Result"]
8+
resources = [aws_guardduty_malware_protection_plan.quarantine.arn]
9+
detail = {
10+
s3ObjectDetails = {
11+
bucketName = [module.s3bucket_quarantine.id]
12+
objectKey = [{ prefix = "pdf-template/" }, { prefix = "test-data/" }]
13+
}
14+
scanResultDetails = {
15+
scanResultStatus = ["NO_THREATS_FOUND"]
16+
}
17+
}
18+
})
19+
}
20+
21+
resource "aws_cloudwatch_event_target" "guardduty_quarantine_scan_no_threats_copy_object" {
22+
rule = aws_cloudwatch_event_rule.guardduty_quarantine_scan_no_threats.name
23+
arn = module.lambda_copy_scanned_object_to_internal.function_arn
24+
role_arn = aws_iam_role.guardduty_quarantine_scan_no_threats.arn
25+
}
26+
27+
resource "aws_iam_role" "guardduty_quarantine_scan_no_threats" {
28+
name = "${local.csi}-quarantine-scan-no-threats"
29+
assume_role_policy = data.aws_iam_policy_document.events_assume_role.json
30+
}
31+
32+
resource "aws_iam_role_policy" "guardduty_quarantine_scan_no_threats" {
33+
name = "${local.csi}-quarantine-scan-no-threats"
34+
role = aws_iam_role.guardduty_quarantine_scan_no_threats.id
35+
policy = data.aws_iam_policy_document.guardduty_quarantine_scan_no_threats.json
36+
}
37+
38+
data "aws_iam_policy_document" "guardduty_quarantine_scan_no_threats" {
39+
version = "2012-10-17"
40+
41+
statement {
42+
sid = "AllowLambdaInvoke"
43+
effect = "Allow"
44+
actions = ["lambda:InvokeFunction"]
45+
resources = [module.lambda_copy_scanned_object_to_internal.function_arn]
46+
}
47+
}

infrastructure/terraform/modules/backend-api/cloudwatch_event_rule_quarantine_scan_result.tf renamed to infrastructure/terraform/modules/backend-api/cloudwatch_event_rule_guardduty_quarantine_scan_result.tf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
resource "aws_cloudwatch_event_rule" "quarantine_guardduty_scan_result" {
2-
name = "${local.csi}-quarantine-scan"
2+
name = "${local.csi}-quarantine-scan-result"
33
description = "Matches quarantine 'GuardDuty Malware Protection Object Scan Result' events"
44

55
event_pattern = jsonencode({
@@ -22,12 +22,12 @@ resource "aws_cloudwatch_event_target" "quarantine_scan_to_update_status" {
2222
}
2323

2424
resource "aws_iam_role" "quarantine_scan_to_update_status" {
25-
name = "${local.csi}-quarantine-scan-to-enrichment"
25+
name = "${local.csi}-quarantine-scan-result"
2626
assume_role_policy = data.aws_iam_policy_document.events_assume_role.json
2727
}
2828

2929
resource "aws_iam_role_policy" "quarantine_scan_to_update_status" {
30-
name = "${local.csi}-quarantine-scan-to-enrichment"
30+
name = "${local.csi}-quarantine-scan-result"
3131
role = aws_iam_role.quarantine_scan_to_update_status.id
3232
policy = data.aws_iam_policy_document.quarantine_scan_to_update_status.json
3333
}

infrastructure/terraform/modules/backend-api/cloudwatch_event_rule_virus_scan_passed.tf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ resource "aws_cloudwatch_event_rule" "virus_scan_passed" {
1111
})
1212
}
1313

14-
resource "aws_cloudwatch_event_target" "scan_passed_copy_object" {
14+
resource "aws_cloudwatch_event_target" "scan_passed_send_to_validation" {
1515
rule = aws_cloudwatch_event_rule.virus_scan_passed.name
16-
arn = module.lambda_copy_scanned_object_to_internal.function_arn
16+
arn = module.lambda_validate_letter_template_files.function_arn
1717
role_arn = aws_iam_role.handle_scan_passed.arn
1818
}
1919

@@ -36,7 +36,7 @@ data "aws_iam_policy_document" "handle_scan_passed" {
3636
effect = "Allow"
3737
actions = ["lambda:InvokeFunction"]
3838
resources = [
39-
module.lambda_copy_scanned_object_to_internal.function_arn,
39+
module.lambda_validate_letter_template_files.function_arn,
4040
]
4141
}
4242
}

infrastructure/terraform/modules/backend-api/locals.tf

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
locals {
22
csi = "${var.csi}-${var.component}"
33

4-
lambdas_source_code_dir = abspath("${path.module}/../../../../lambdas")
4+
monorepo_root = abspath("${path.module}/../../../..")
5+
lambdas_source_code_dir = abspath("${local.monorepo_root}/lambdas")
6+
pdfjs_layer_source_code_dir = abspath("${local.monorepo_root}/layers/pdfjs")
57

68
openapi_spec = templatefile("${path.module}/spec.tmpl.json", {
79
AWS_REGION = var.region
@@ -23,6 +25,7 @@ locals {
2325
set_file_virus_scan_status = "src/templates/set-letter-file-virus-scan-status.ts"
2426
copy_scanned_object_to_internal = "src/templates/copy-scanned-object-to-internal.ts"
2527
delete_failed_scanned_object = "src/templates/delete-failed-scanned-object.ts"
28+
validate_letter_template_files = "src/templates/validate-letter-template-files.ts"
2629
template_client = "src/index.ts"
2730
}
2831

infrastructure/terraform/modules/backend-api/module_build_template_lambda.tf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ module "build_template_lambda" {
1111
local.backend_lambda_entrypoints.set_file_virus_scan_status,
1212
local.backend_lambda_entrypoints.copy_scanned_object_to_internal,
1313
local.backend_lambda_entrypoints.delete_failed_scanned_object,
14+
local.backend_lambda_entrypoints.validate_letter_template_files,
1415
]
16+
17+
externals = ["pdfjs-dist"]
1518
}
1619

1720
module "build_template_client" {

infrastructure/terraform/modules/backend-api/module_lambda_copy_scanned_object_to_internal.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ data "aws_iam_policy_document" "copy_scanned_object_to_internal" {
3232

3333
actions = [
3434
"s3:GetObject",
35+
"s3:GetObjectVersion",
3536
"s3:GetObjectTagging",
37+
"s3:GetObjectVersionTagging",
3638
]
3739

3840
resources = ["${module.s3bucket_quarantine.arn}/*"]
@@ -44,7 +46,9 @@ data "aws_iam_policy_document" "copy_scanned_object_to_internal" {
4446

4547
actions = [
4648
"s3:PutObject",
49+
"s3:PutObjectVersion",
4750
"s3:PutObjectTagging",
51+
"s3:PutObjectVersionTagging",
4852
]
4953

5054
resources = ["${module.s3bucket_internal.arn}/*"]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module "lambda_layer_pdfjs" {
2+
source = "../lambda_layer"
3+
name = "${local.csi}-nodejs20-pdfjs-dist"
4+
description = "pdfjs-dist dependencies for Node.js v20"
5+
source_code_dir = local.pdfjs_layer_source_code_dir
6+
nodejs_runtime_version = "20"
7+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
module "lambda_validate_letter_template_files" {
2+
source = "../lambda-function"
3+
description = "Validates content of letter template files"
4+
5+
dead_letter_target_arn = module.sqs_validate_letter_template_files_dlq.sqs_queue_arn
6+
execution_role_policy_document = data.aws_iam_policy_document.validate_letter_template_files.json
7+
filename = module.build_template_lambda.zips[local.backend_lambda_entrypoints.validate_letter_template_files].path
8+
function_name = "${local.csi}-validate-letter-template-files"
9+
handler = "validate-letter-template-files.handler"
10+
log_retention_in_days = var.log_retention_in_days
11+
source_code_hash = module.build_template_lambda.zips[local.backend_lambda_entrypoints.validate_letter_template_files].base64sha256
12+
timeout = 10
13+
memory_size = 512
14+
layer_arns = [module.lambda_layer_pdfjs.layer_arn]
15+
environment_variables = local.backend_lambda_environment_variables
16+
}
17+
18+
data "aws_iam_policy_document" "validate_letter_template_files" {
19+
statement {
20+
sid = "AllowDynamoAccess"
21+
effect = "Allow"
22+
23+
actions = [
24+
"dynamodb:GetItem",
25+
"dynamodb:UpdateItem",
26+
]
27+
28+
resources = [
29+
aws_dynamodb_table.templates.arn,
30+
]
31+
}
32+
33+
statement {
34+
sid = "AllowKMSAccessDynamo"
35+
effect = "Allow"
36+
37+
actions = [
38+
"kms:Decrypt",
39+
"kms:DescribeKey",
40+
"kms:Encrypt",
41+
"kms:GenerateDataKey*",
42+
"kms:ReEncrypt*",
43+
]
44+
45+
resources = [
46+
local.dynamodb_kms_key_arn
47+
]
48+
}
49+
50+
statement {
51+
sid = "AllowS3InternalGetObject"
52+
effect = "Allow"
53+
54+
actions = [
55+
"s3:GetObject"
56+
]
57+
58+
resources = ["${module.s3bucket_internal.arn}/*"]
59+
}
60+
61+
statement {
62+
sid = "AllowKMSAccessSQSS3"
63+
effect = "Allow"
64+
65+
actions = [
66+
"kms:Decrypt",
67+
"kms:GenerateDataKey",
68+
]
69+
70+
resources = [
71+
var.kms_key_arn
72+
]
73+
}
74+
75+
statement {
76+
sid = "AllowSQSDLQ"
77+
effect = "Allow"
78+
79+
actions = [
80+
"sqs:SendMessage",
81+
]
82+
83+
resources = [
84+
module.sqs_validate_letter_template_files_dlq.sqs_queue_arn,
85+
]
86+
}
87+
}

0 commit comments

Comments
 (0)