Skip to content

Commit 3a8278e

Browse files
authored
Merge branch 'main' into PRM-823
2 parents 14d8e8b + d78b109 commit 3a8278e

19 files changed

Lines changed: 1790 additions & 7 deletions

.github/actions/tf-plan-apply/action.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ runs:
6363
aws-region: ${{ inputs.aws_region }}
6464
mask-aws-account-id: true
6565

66+
- name: Generate CloudFront function
67+
run:
68+
npm ci
69+
npm run test
70+
npm run build
71+
working-directory: ./infrastructure/code
72+
shell: bash
73+
6674
- name: Setup Terraform
6775
uses: hashicorp/setup-terraform@v4
6876
with:

.github/workflows/automated-sonarqube-cloud-analysis.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ jobs:
2121
with:
2222
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
2323

24+
- name: Set up Node.js
25+
uses: actions/setup-node@v6
26+
with:
27+
node-version: '24'
28+
29+
- name: Install and test
30+
working-directory: infrastructure/code
31+
run: |
32+
npm ci
33+
npm test
34+
2435
- name: SonarQube Cloud Scan
2536
uses: SonarSource/sonarqube-scan-action@v7
2637
env:

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ tfplan
3636
.idea/
3737
.vscode/
3838
venv/
39+
coverage/
3940

4041
#Ignore certificates
4142
scripts/csrs
4243
scripts/keys
44+
infrastructure/code/dist

infrastructure/cloudfront.tf

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ module "cloudfront_firewall_waf_v2" {
2222
}
2323

2424
resource "aws_cloudfront_distribution" "s3_presign_mask" {
25-
price_class = "PriceClass_100"
26-
25+
price_class = "PriceClass_100"
2726
aliases = [local.cloudfront_full_domain_name]
2827
wait_for_deployment = false
28+
2929
origin {
3030
domain_name = module.ndr-lloyd-george-store.bucket_regional_domain_name
3131
origin_id = module.ndr-lloyd-george-store.bucket_id
3232
origin_access_control_id = aws_cloudfront_origin_access_control.s3.id
3333
}
34+
3435
enabled = true
3536
is_ipv6_enabled = true
3637

@@ -42,6 +43,12 @@ resource "aws_cloudfront_distribution" "s3_presign_mask" {
4243
cache_policy_id = local.cloudfront_cache_policy_id
4344
origin_request_policy_id = local.cloudfront_viewer_policy_id
4445

46+
function_association {
47+
# IF THIS IS EVER COPIED TO A NEW BEHAVIOR, THE FUNCTION MUST BE UPDATED TO ALLOW THE NEW PATH
48+
event_type = "viewer-request"
49+
function_arn = aws_cloudfront_function.block_invalid_urls.arn
50+
}
51+
4552
lambda_function_association {
4653
event_type = "origin-request"
4754
lambda_arn = module.edge-presign-lambda.qualified_arn
@@ -63,6 +70,12 @@ resource "aws_cloudfront_distribution" "s3_presign_mask" {
6370
cache_policy_id = local.cloudfront_cache_policy_id
6471
origin_request_policy_id = local.cloudfront_viewer_policy_id
6572

73+
function_association {
74+
# IF THIS IS EVER COPIED TO A NEW BEHAVIOR, THE FUNCTION MUST BE UPDATED TO ALLOW THE NEW PATH
75+
event_type = "viewer-request"
76+
function_arn = aws_cloudfront_function.block_invalid_urls.arn
77+
}
78+
6679
lambda_function_association {
6780
event_type = "origin-request"
6881
lambda_arn = module.edge-presign-lambda.qualified_arn
@@ -84,6 +97,12 @@ resource "aws_cloudfront_distribution" "s3_presign_mask" {
8497
cache_policy_id = local.cloudfront_cache_policy_id
8598
origin_request_policy_id = local.cloudfront_uploader_policy_id
8699

100+
function_association {
101+
# IF THIS IS EVER COPIED TO A NEW CLOUDFRONT [ordered_cache_behavior] BEHAVIOR, THE FUNCTION MUST BE UPDATED TO ALLOW THE NEW PATH
102+
event_type = "viewer-request"
103+
function_arn = aws_cloudfront_function.block_invalid_urls.arn
104+
}
105+
87106
lambda_function_association {
88107
event_type = "origin-request"
89108
lambda_arn = module.edge-presign-lambda.qualified_arn
@@ -102,11 +121,20 @@ resource "aws_cloudfront_distribution" "s3_presign_mask" {
102121
locations = local.allow_us_comms ? ["GB", "US"] : ["GB"]
103122
}
104123
}
124+
105125
web_acl_id = try(module.cloudfront_firewall_waf_v2[0].arn, "")
106126

107127
depends_on = [aws_acm_certificate_validation.cloudfront]
108128
}
109129

130+
resource "aws_cloudfront_function" "block_invalid_urls" {
131+
name = "${terraform.workspace}-block-invalid-urls"
132+
runtime = "cloudfront-js-2.0"
133+
comment = "Blocks invalid URL requests"
134+
publish = true
135+
code = file("${path.module}/code/dist/block-invalid-urls.js")
136+
}
137+
110138
resource "aws_cloudfront_origin_request_policy" "viewer" {
111139
count = local.is_sandbox ? 0 : 1
112140
name = "${terraform.workspace}_BlockQueriesAndAllowViewer"

infrastructure/code/.gitignore

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

infrastructure/code/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# CloudFront Functions
2+
3+
TypeScript source for CloudFront Functions deployed by Terraform.
4+
5+
## Runtime constraints
6+
7+
These functions run on CloudFront Functions 2.0 (QuickJS), which rejects:
8+
9+
- ES modules (`import`/`export`)
10+
- `require()` for user code (built-ins like `crypto`, `querystring`, `buffer` only)
11+
- `eval`, `Function` constructor, `setTimeout`, network, filesystem
12+
13+
So each source file under `src/` must be a flat script with a top-level
14+
`function handler(event)`. No imports, no exports. Types come from
15+
triple-slash references only.
16+
17+
## Layout
18+
19+
```text
20+
src/ TypeScript sources — one file per deployed function
21+
test/ Vitest suites; load the compiled artifact via node:vm
22+
so tests exercise the exact script CloudFront will run
23+
dist/ Build output. Committed. Terraform reads from here.
24+
```
25+
26+
## Commands
27+
28+
```bash
29+
npm run build compile src/ → dist/
30+
npm test build then run vitest
31+
npm run test:watch vitest in watch mode
32+
```
33+
34+
Rebuild and commit `dist/` whenever a source file changes. Terraform reads
35+
`dist/block-invalid-urls.js` at plan time.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
function handler(event) {
2+
const uri = event.request.uri;
3+
const allowedPattern = /^\/(review\/|upload\/)?[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/;
4+
if (allowedPattern.test(uri)) {
5+
return event.request;
6+
}
7+
return {
8+
statusCode: 403,
9+
statusDescription: 'Forbidden',
10+
headers: {
11+
'content-type': { value: 'text/plain' }
12+
},
13+
body: 'Access Denied'
14+
};
15+
}

0 commit comments

Comments
 (0)