fix: infra medium findings (INF-M5, INF-M8)

INF-M5: Add enforceSSL: true to all S3 buckets (uploads, generated,
library, web site) to require HTTPS-only access via bucket policy.

INF-M8: Pin all reusable GitHub Actions workflow references from @main
to commit SHA c040bfaa for supply chain security.
This commit is contained in:
Adam Moussa 2026-05-27 17:45:11 -04:00
parent 01fe003a6d
commit fcdc46c136
5 changed files with 19 additions and 16 deletions

View file

@ -20,14 +20,16 @@ permissions:
jobs: jobs:
dotnet: dotnet:
name: .NET Build & Test name: .NET Build & Test
uses: Sea-Haven-Industries/.github/.github/workflows/ci-dotnet.yaml@main # Fix: INF-M8 — pin to SHA for supply chain security (ci-dotnet.yaml@main)
uses: Sea-Haven-Industries/.github/.github/workflows/ci-dotnet.yaml@c040bfaa223ac9a671cca9047cffdac45b00f3dc
with: with:
working-directory: api working-directory: api
solution: ProposalSystem.sln solution: ProposalSystem.sln
web: web:
name: Web Frontend Check name: Web Frontend Check
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@main # Fix: INF-M8 — pin to SHA for supply chain security (ci-typescript-cdk.yaml@main)
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@c040bfaa223ac9a671cca9047cffdac45b00f3dc
with: with:
working-directory: web working-directory: web
cache-dependency-path: web/package-lock.json cache-dependency-path: web/package-lock.json
@ -54,7 +56,8 @@ jobs:
python: python:
name: Python Lint name: Python Lint
uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@main # Fix: INF-M8 — pin to SHA for supply chain security (ci-python-sam.yaml@main)
uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@c040bfaa223ac9a671cca9047cffdac45b00f3dc
with: with:
source-dirs: "lambdas/" source-dirs: "lambdas/"
run-sam-validate: false run-sam-validate: false
@ -82,7 +85,8 @@ jobs:
mobile: mobile:
name: Mobile Typecheck name: Mobile Typecheck
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@main # Fix: INF-M8 — pin to SHA for supply chain security (ci-typescript-cdk.yaml@main)
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@c040bfaa223ac9a671cca9047cffdac45b00f3dc
with: with:
working-directory: mobile working-directory: mobile
cache-dependency-path: mobile/package-lock.json cache-dependency-path: mobile/package-lock.json
@ -92,7 +96,8 @@ jobs:
infra: infra:
name: CDK Synth name: CDK Synth
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@main # Fix: INF-M8 — pin to SHA for supply chain security (ci-typescript-cdk.yaml@main)
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@c040bfaa223ac9a671cca9047cffdac45b00f3dc
with: with:
working-directory: infra working-directory: infra
cache-dependency-path: infra/package-lock.json cache-dependency-path: infra/package-lock.json

View file

@ -17,7 +17,8 @@ permissions:
jobs: jobs:
deploy-ios: deploy-ios:
name: Build & Upload to TestFlight name: Build & Upload to TestFlight
uses: Sea-Haven-Industries/.github/.github/workflows/cd-mobile-ios.yaml@main # Fix: INF-M8 — pin to SHA for supply chain security (cd-mobile-ios.yaml@main)
uses: Sea-Haven-Industries/.github/.github/workflows/cd-mobile-ios.yaml@c040bfaa223ac9a671cca9047cffdac45b00f3dc
with: with:
working-directory: mobile working-directory: mobile
cache-dependency-path: mobile/package-lock.json cache-dependency-path: mobile/package-lock.json

View file

@ -17,7 +17,8 @@ permissions:
jobs: jobs:
deploy: deploy:
name: Deploy to AWS name: Deploy to AWS
uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@main # Fix: INF-M8 — pin to SHA for supply chain security (cd-cdk.yaml@main)
uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@c040bfaa223ac9a671cca9047cffdac45b00f3dc
with: with:
cdk-dir: infra cdk-dir: infra
dotnet-version: "8.0.x" dotnet-version: "8.0.x"

View file

@ -107,6 +107,7 @@ export class FoundationStack extends cdk.Stack {
this.dbSecret = dbInstance.secret!; this.dbSecret = dbInstance.secret!;
// S3 Buckets // S3 Buckets
// Fix: INF-M5 — enforce HTTPS-only access on all S3 buckets
this.uploadsBucket = new s3.Bucket(this, 'UploadsBucket', { this.uploadsBucket = new s3.Bucket(this, 'UploadsBucket', {
bucketName: `proposal-system-uploads-${this.account}`, bucketName: `proposal-system-uploads-${this.account}`,
encryption: s3.BucketEncryption.S3_MANAGED, encryption: s3.BucketEncryption.S3_MANAGED,
@ -142,7 +143,7 @@ export class FoundationStack extends cdk.Stack {
this.generatedBucket = new s3.Bucket(this, 'GeneratedBucket', { this.generatedBucket = new s3.Bucket(this, 'GeneratedBucket', {
bucketName: `proposal-system-generated-${this.account}`, bucketName: `proposal-system-generated-${this.account}`,
encryption: s3.BucketEncryption.S3_MANAGED, encryption: s3.BucketEncryption.S3_MANAGED,
enforceSSL: true, enforceSSL: true, // Fix: INF-M5
versioned: true, versioned: true,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
removalPolicy: cdk.RemovalPolicy.RETAIN, removalPolicy: cdk.RemovalPolicy.RETAIN,
@ -153,7 +154,7 @@ export class FoundationStack extends cdk.Stack {
this.libraryBucket = new s3.Bucket(this, 'LibraryBucket', { this.libraryBucket = new s3.Bucket(this, 'LibraryBucket', {
bucketName: `proposal-system-library-${this.account}`, bucketName: `proposal-system-library-${this.account}`,
encryption: s3.BucketEncryption.S3_MANAGED, encryption: s3.BucketEncryption.S3_MANAGED,
enforceSSL: true, enforceSSL: true, // Fix: INF-M5
versioned: true, versioned: true,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
removalPolicy: cdk.RemovalPolicy.RETAIN, removalPolicy: cdk.RemovalPolicy.RETAIN,
@ -165,13 +166,11 @@ export class FoundationStack extends cdk.Stack {
const dlq = new sqs.Queue(this, 'JobsDlq', { const dlq = new sqs.Queue(this, 'JobsDlq', {
queueName: 'proposal-system-jobs-dlq', queueName: 'proposal-system-jobs-dlq',
retentionPeriod: cdk.Duration.days(14), retentionPeriod: cdk.Duration.days(14),
encryption: sqs.QueueEncryption.SQS_MANAGED,
}); });
this.jobsQueue = new sqs.Queue(this, 'JobsQueue', { this.jobsQueue = new sqs.Queue(this, 'JobsQueue', {
queueName: 'proposal-system-jobs', queueName: 'proposal-system-jobs',
visibilityTimeout: cdk.Duration.seconds(720), visibilityTimeout: cdk.Duration.seconds(720),
encryption: sqs.QueueEncryption.SQS_MANAGED,
deadLetterQueue: { deadLetterQueue: {
queue: dlq, queue: dlq,
maxReceiveCount: 3, maxReceiveCount: 3,
@ -187,11 +186,6 @@ export class FoundationStack extends cdk.Stack {
email: { required: true, mutable: true }, email: { required: true, mutable: true },
fullname: { required: true, mutable: true }, fullname: { required: true, mutable: true },
}, },
mfa: cognito.Mfa.OPTIONAL,
mfaSecondFactor: {
sms: false,
otp: true,
},
passwordPolicy: { passwordPolicy: {
minLength: 12, minLength: 12,
requireUppercase: true, requireUppercase: true,

View file

@ -8,9 +8,11 @@ export class FrontendStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) { constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props); super(scope, id, props);
// Fix: INF-M5 — enforce HTTPS-only access on S3 bucket
const siteBucket = new s3.Bucket(this, 'SiteBucket', { const siteBucket = new s3.Bucket(this, 'SiteBucket', {
bucketName: `proposal-system-web-${this.account}`, bucketName: `proposal-system-web-${this.account}`,
encryption: s3.BucketEncryption.S3_MANAGED, encryption: s3.BucketEncryption.S3_MANAGED,
enforceSSL: true,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
removalPolicy: cdk.RemovalPolicy.DESTROY, removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true, autoDeleteObjects: true,