Skip to content

Releases: gofastercloud/terraform-secure-upload

v0.7.2

Choose a tag to compare

@gofastercloud gofastercloud released this 11 Feb 00:45
09ab835

Fixed

  • Scan summary hardcoded NO_THREATS_FOUND for all quarantine paths — _build_scan_summary() always reported GuardDuty as NO_THREATS_FOUND and downstream scanners as clean, even when GuardDuty found threats or VT/PI triggered quarantine. The function now accepts the actual scan results: GuardDuty status is derived from the routing reason, VT results are passed through, PI score is included when it triggered quarantine, and scanners that didn't run show not_checked instead of clean.

v0.7.1

Choose a tag to compare

@gofastercloud gofastercloud released this 11 Feb 00:45
1faad8f

Fixed

  • Discord and ServiceNow webhooks missing scan results — The webhook forwarder Lambda was not extracting the scans field from quarantine SNS messages, so Discord embeds and ServiceNow incidents only showed threat names without GuardDuty status, VirusTotal details (positives/total/SHA-256), or prompt injection findings. Now both integrations include full scan results.

v0.7.0 — Parallel VirusTotal Scanning

Choose a tag to compare

@gofastercloud gofastercloud released this 11 Feb 00:45
d980060

Changed

  • VirusTotal scanning runs in parallel with GuardDuty — The VirusTotal hash lookup now triggers immediately when a file lands in the ingress bucket via an EventBridge rule on S3 Object Created events, running concurrently with GuardDuty's deeper scan. Previously, VT ran sequentially inside the file router after GuardDuty completed. VT results are stored as S3 object tags (vt-status, vt-positives, vt-total, vt-sha256) which the file router reads when making its routing decision. If VT hasn't finished by the time the file router runs (unlikely but possible), it falls back to synchronous invocation.
  • Richer notification messages — Quarantine alerts and egress notifications now include detailed scan results from all scanners (GuardDuty status, VirusTotal positives/total/SHA-256, prompt injection score) in a structured scans field.

Added

  • S3 EventBridge notifications on ingress bucket — aws_s3_bucket_notification resource enables EventBridge delivery for Object Created events.
  • EventBridge rule for VT scanner — new rule + target + permission (conditional on enable_virustotal_scanning) triggers VT scanner Lambda on S3 uploads.
  • S3 object tagging by VT scanner — VT scanner writes results as object tags after scanning, enabling the file router to read results without a synchronous invoke.
  • Test file generator — test-files/generate.py (uv-runnable) produces clean, EICAR, and prompt injection test files in .txt, .pdf, and .docx formats for pipeline validation.

v0.6.0 — VirusTotal, Audit Trail & Egress Notifications

Choose a tag to compare

@gofastercloud gofastercloud released this 11 Feb 00:45
0ca8e50

Added

  • VirusTotal hash lookup scanning — optional SHA-256 hash check against the VirusTotal API. Files that pass GuardDuty are checked against VT; those with detections at or above virustotal_threshold are quarantined. API key stored as SSM SecureString.
  • File processing audit trail — optional DynamoDB table records every file at each pipeline stage (received, GuardDuty, VirusTotal, prompt injection, routed) with configurable TTL retention via audit_trail_retention_days.
  • Egress notifications — optional SNS topic fires when clean files reach the egress bucket, enabling downstream automation.
  • CloudWatch dashboard metric filters — expanded dashboard with VirusTotal scan metrics, prompt injection metrics, and egress notification tracking.
  • New variables — enable_virustotal_scanning, virustotal_api_key, virustotal_threshold, enable_egress_notifications, egress_notification_emails, enable_audit_trail, audit_trail_retention_days, enable_cloudwatch_dashboard.
  • New outputs — virustotal_scanner_function_arn, egress_sns_topic_arn, audit_trail_table_arn, audit_trail_table_name, cloudwatch_dashboard_arn.

v0.5.1

Choose a tag to compare

@gofastercloud gofastercloud released this 10 Feb 06:32
c27ef71

Fixes

  • Scope chatbot IAM policy: CloudWatch Logs access now restricted to this module's log groups (/aws/lambda/${name_prefix}-*) instead of account-wide Resource: "*"
  • Fix inconsistent tagging: All integration resources now use local.default_tags (adds missing Module and ManagedBy tags)
  • Isolate webhook integration failures: Discord and ServiceNow calls are now independent — one failing no longer blocks the other

v0.5.0 — Prompt Injection Scanning

Choose a tag to compare

@gofastercloud gofastercloud released this 10 Feb 05:32
a1711d2

What's New

Prompt Injection Scanning

Optional AI-powered scanning of uploaded documents for prompt injection attacks. When enabled, files that pass GuardDuty malware scanning are additionally scanned using the protectai/deberta-v3-base-prompt-injection-v2 ONNX model before reaching the egress bucket.

Supported formats: PDF, DOCX, PPTX, and plain text (TXT, CSV, MD, JSON, XML, HTML, YAML, LOG). Non-text files pass through with score 0.

How it works: The file router invokes a container-image Lambda synchronously. Text is extracted, tokenized into overlapping 512-token chunks, and scored by the model. Files scoring above the configurable threshold (default 80) are quarantined with an SNS alert.

Usage

module "secure_upload" {
  source = "gofastercloud/secure-upload/aws"

  name_prefix                      = "myapp"
  enable_prompt_injection_scanning = true

  # Optional tuning
  prompt_injection_threshold = 80   # 0-100, default 80
  prompt_injection_memory_size = 3008  # more memory = more CPU = faster cold starts
}

New Variables

Variable Default Description
enable_prompt_injection_scanning false Enable the scanning pipeline
prompt_injection_threshold 80 Score (0-100) above which files are quarantined
prompt_injection_memory_size 3008 Scanner Lambda memory in MB
prompt_injection_timeout 120 Scanner Lambda timeout in seconds
prompt_injection_reserved_concurrency 5 Scanner Lambda concurrency
prompt_injection_image_uri null BYO pre-built container image

Other Changes

  • File router timeout auto-adjusts to scanner_timeout + 30s when scanning is enabled
  • ECR repository with KMS encryption and lifecycle policy for scanner images
  • 7 new validation tests (49 total)
  • Documentation updated (README, ARCHITECTURE.md, CHANGELOG.md)

Full Changelog: v0.4.0...v0.5.0

v0.4.0 — Chat & Incident Integrations

Choose a tag to compare

@gofastercloud gofastercloud released this 10 Feb 04:24
c530a50

Added

  • Slack integration — AWS Chatbot channel configuration for malware alerts
  • Microsoft Teams integration — AWS Chatbot channel configuration for malware alerts
  • PagerDuty integration — SNS HTTPS subscription to PagerDuty Events API
  • VictorOps / Splunk On-Call integration — SNS HTTPS subscription to VictorOps REST endpoint
  • Discord integration — Lambda webhook forwarder with rich embeds; webhook URL stored in SSM Parameter Store (SecureString, KMS-encrypted)
  • ServiceNow integration — Lambda webhook forwarder that creates incidents via REST API; credentials fetched from Secrets Manager at runtime
  • Integration documentation — new integrations.md with detailed setup instructions for all six integrations
  • 11 new validation tests — integration variable validation coverage (42 total tests)

All integrations are optional and disabled by default. See integrations.md for setup instructions.

v0.3.0 — End-to-end tested

Choose a tag to compare

@gofastercloud gofastercloud released this 10 Feb 04:08
91b4174

What's Changed

Three bugs discovered and fixed during end-to-end smoke testing:

Fixed

  • S3 bucket policy StringNotEqualsIfExists incorrect for Deny statements — The v0.2.1 fix was wrong: StringNotEqualsIfExists on a Deny evaluates to true when the condition key is absent, which still blocks uploads without explicit encryption headers. Replaced with StringNotEquals + Null condition guard across all three buckets (6 statements). The Deny now only fires when the header is explicitly present with a non-KMS value; absent headers fall through to bucket default SSE-KMS.

  • SFTP user IAM trust policy used wrong ARN type — Transfer Family passes a user ARN (arn:aws:transfer:region:account:user/server-id/username), not a server ARN, when assuming the data access role. Changed aws:SourceArn from server/* to user/server-id/*.

  • SFTP ListBucket condition too restrictive — The s3:prefix condition only allowed uploads/dave/* but Transfer Family also needs to stat the home directory prefix itself (uploads/dave without trailing slash). Added the bare prefix as a second allowed value.

Verified

End-to-end pipeline tested:

Test GuardDuty Result Lambda Route SNS Alert
Clean text file (SFTP upload) NO_THREATS_FOUND → egress bucket No
EICAR test file (SFTP upload) THREATS_FOUND → quarantine bucket Yes (email delivered)
SFTP egress download — File downloaded successfully —

Full Changelog: v0.2.2...v0.3.0

v0.2.2

Choose a tag to compare

@gofastercloud gofastercloud released this 10 Feb 03:21
0919057

What's Changed

Fixed

  • KMS key policy missing CloudWatch Logs service principal — CloudWatch Logs uses a regional service principal (logs.<region>.amazonaws.com) with a kms:EncryptionContext condition. The Lambda log group failed to create with AccessDeniedException. Added a dedicated AllowCloudWatchLogs statement to the KMS key policy.

  • GuardDuty Malware Protection Plan creation failed — The GuardDuty IAM role was missing S3 bucket notification permissions (s3:GetBucketNotification, s3:PutBucketNotification) and EventBridge managed rule permissions (events:PutRule, events:PutTargets, events:DeleteRule, events:RemoveTargets, events:DescribeRule).

  • Transfer Family user creation failed with trailing slash — Home directory mapping targets included a trailing / which Transfer Family rejects. Fixed with trimsuffix().

Changed

  • lambda_reserved_concurrency now accepts -1 — Uses unreserved account concurrency, necessary for accounts where reserving even 10 would drop below the 50-unreserved minimum.

Full Changelog: v0.2.1...v0.2.2

v0.2.1

Choose a tag to compare

@gofastercloud gofastercloud released this 10 Feb 03:04
a77c217

What's Changed

Fixed

  • S3 bucket policy blocks AWS service uploads — Changed StringNotEquals to StringNotEqualsIfExists in DenyNonKMSEncryption and DenyWrongKMSKey policy statements across all three buckets (ingress, egress, quarantine). Services that rely on bucket default encryption and omit x-amz-server-side-encryption headers (e.g. AWS Transfer Family, GuardDuty, Lambda) were getting AccessDenied. The IfExists variant only evaluates the condition when the header is present, allowing headless uploads to fall through to bucket default SSE-KMS.

Changed

  • Bucket names include hashed account ID for global uniqueness — S3 bucket names changed from <prefix>-<type> to <prefix>-<hash>-<type> where <hash> is the first 8 characters of sha256(account_id). This prevents name collisions across AWS accounts without exposing the raw account ID. Breaking change: existing buckets will be recreated.

  • IAM wildcards replaced with explicit actions — kms:GenerateDataKey*, kms:ReEncrypt*, transfer:*, and ec2:* replaced with enumerated action lists in the KMS key policy and README SCP recommendations.

Upgrading

This release contains two breaking changes:

  1. Bucket names changed — All four buckets (ingress, egress, quarantine, logs) now include an account hash in the name. Terraform will plan to destroy and recreate them. Back up your data before upgrading or use terraform state mv to migrate.

  2. KMS key policy updated — The AllowServiceUsage statement now uses explicit actions instead of wildcards. This is a non-destructive policy update.

Full Changelog: v0.2.0...v0.2.1