Skip to content

skpr/cve-triage-lambda

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cve-triage-lambda

An AWS Lambda that fetches recent CVEs from the NVD API, prefilters them, and uses AWS Bedrock (Claude) to triage their relevance to your hosting platform. The results are delivered to Slack as:

  1. A full, styled HTML report uploaded as a file.
  2. A rich Block Kit summary message (severity counts + top applicable CVEs).

The Lambda is intended to be run on an EventBridge schedule (e.g. daily).

How it works

EventBridge (cron) ──▶ Lambda
                         │
                         ├─ NVD API        (fetch recent CVEs)
                         ├─ prefilter      (CVSS threshold + keywords)
                         ├─ AWS Bedrock    (AI triage → structured results)
                         ├─ HTML report    (html/template, embedded)
                         └─ Slack          (upload file + Block Kit summary)

Pipeline source layout:

Package Responsibility
cmd/lambda Lambda entry point (lambda.Start)
internal/app Shared pipeline: load context → fetch → filter → triage
internal/config Env-driven configuration (LoadFromEnv)
internal/nvd NVD 2.0 API client
internal/filter CVSS + keyword prefilter
internal/triage Bedrock Converse triage
internal/render HTML report + summary computation
internal/slack File upload + Block Kit summary delivery

The platform context (internal/app/platform.md) is embedded into the binary, so the Lambda is self-contained. Override it at runtime with the PLATFORM_PROMPT / PLATFORM_PROMPT_FILE env vars if needed.

Configuration

All configuration is via environment variables.

Variable Required Default Description
SLACK_BOT_TOKEN yes Slack bot token (xoxb-...)
SLACK_CHANNEL_ID yes Target Slack channel ID
PLATFORM_NAME no platform Label used in titles / filenames
AWS_REGION no ap-southeast-2 Region for Bedrock (set automatically by Lambda)
BEDROCK_MODEL_ID no apac.anthropic.claude-sonnet-4-5-20250514-v1:0 Bedrock model / inference profile ID
CVE_BATCH_SIZE no 25 CVEs per Bedrock call
CVE_MAX_CONCURRENCY no 4 Max concurrent Bedrock batch calls (increase for faster runs; reduce if throttled)
CVE_WINDOW no 24h Lookback window (Go duration, e.g. 168h for 7 days)
CVE_KEYWORDS no (from platform.md) Comma-separated keyword include-list
NVD_API_KEY no NVD API key (raises rate limits)
PLATFORM_PROMPT no Inline platform context (overrides file/embed)
PLATFORM_PROMPT_FILE no platform.md Path to platform context file
OUTPUT_FORMAT no table Unused by the Lambda path

If CVE_KEYWORDS is empty, keywords are extracted from the Keywords: line in the platform context.

Building & releasing

This project uses Mise to manage tooling and build tasks, matching the approach used across skpr's Lambda projects.

Install Mise and tools

curl https://mise.run | sh
mise install

Build the Lambda zip

mise run build

This produces lambda-handler.zip containing the bootstrap binary (GOOS=linux GOARCH=amd64 CGO_ENABLED=0, built with -tags lambda.norpc).

Release

Pushing a v0.* tag triggers the Publish Artifacts GitHub Actions workflow, which builds the zip and attaches it to a GitHub Release automatically.

git tag v0.1.0
git push origin v0.1.0

Manual build (without Mise)

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags lambda.norpc -o bootstrap ./cmd/lambda
zip lambda-handler.zip bootstrap
rm bootstrap

Deploying

Create the function:

aws lambda create-function \
  --function-name cve-triage \
  --runtime provided.al2023 \
  --architectures x86_64 \
  --handler bootstrap \
  --role arn:aws:iam::<account-id>:role/cve-triage-lambda \
  --timeout 600 \
  --memory-size 256 \
  --zip-file fileb://lambda-handler.zip \
  --environment "Variables={SLACK_BOT_TOKEN=xoxb-...,SLACK_CHANNEL_ID=C0123456789,PLATFORM_NAME=Skpr Hosting,BEDROCK_MODEL_ID=apac.anthropic.claude-sonnet-4-5-20250514-v1:0}"

Update an existing function:

aws lambda update-function-code \
  --function-name cve-triage \
  --zip-file fileb://lambda-handler.zip

Schedule (EventBridge)

aws events put-rule \
  --name cve-triage-daily \
  --schedule-expression "rate(1 day)"

aws lambda add-permission \
  --function-name cve-triage \
  --statement-id cve-triage-eventbridge \
  --action lambda:InvokeFunction \
  --principal events.amazonaws.com \
  --source-arn arn:aws:events:<region>:<account-id>:rule/cve-triage-daily

aws events put-targets \
  --rule cve-triage-daily \
  --targets "Id"="cve-triage","Arn"="arn:aws:lambda:<region>:<account-id>:function:cve-triage"

Required permissions

IAM (Lambda execution role)

  • bedrock:InvokeModel on the model / inference profile ARN.
  • logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents (or attach AWSLambdaBasicExecutionRole).

Example inline policy statement for Bedrock:

{
  "Effect": "Allow",
  "Action": "bedrock:InvokeModel",
  "Resource": [
    "arn:aws:bedrock:*::foundation-model/anthropic.*",
    "arn:aws:bedrock:*:<account-id>:inference-profile/apac.anthropic.*"
  ]
}

Slack app scopes

The bot token needs:

  • files:write — upload the HTML report.
  • chat:write — post the summary message.

Invite the bot to the target channel, then use that channel's ID for SLACK_CHANNEL_ID.

Local testing

The handler reads everything from the environment, so you can exercise the pipeline locally with valid AWS credentials and a Slack token:

export SLACK_BOT_TOKEN=xoxb-...
export SLACK_CHANNEL_ID=C0123456789
export AWS_REGION=ap-southeast-2
export PLATFORM_NAME="Skpr Hosting"

go run ./cmd/lambda    # note: lambda.Start expects the Lambda runtime API;
                       # use the AWS RIE or `sam local invoke` for true local runs.

For a faithful local invocation, use the AWS Lambda Runtime Interface Emulator or sam local invoke.

Tests

go test ./...

About

A Lambda for posting routine CVE triages

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors