If you discover a potential security issue in this project, please notify AWS/Amazon Security via the vulnerability reporting page. Please do not create a public GitHub issue.
Streamlit UI (demo/app.py)
|
| HTTPS/TLS 1.2+ (SigV4-signed)
v
Amazon Bedrock AgentCore Runtime (agent code, multi-agent orchestration)
|
| IAM auth (SigV4)
v
Amazon Bedrock AgentCore Gateway (MCP protocol, tool discovery)
|
| IAM auth
v
AWS Lambda (shared tool handlers)
|
| HTTPS/TLS 1.2+
v
External APIs (HN, YouTube, dev.to, Reddit, GitHub, etc.)
Amazon DynamoDB <-- AgentCore Runtime (leads, score rows, frontier claims)
AWS Secrets Manager <-- Lambda only (CMK-encrypted API keys)
-
Authentication and authorization -- IAM authentication with SigV4 signing is enforced on Amazon API Gateway, Amazon Bedrock AgentCore Gateway, and Amazon Bedrock AgentCore Runtime. No anonymous access is permitted.
-
Secrets management -- Four third-party credentials (YouTube Data API, Product Hunt, GitHub token, Reddit OAuth2). The CDK stack creates these secrets empty and CMK-encrypted under the
social-intel/prefix. Operators populate values post-deploy viaaws secretsmanager put-secret-value. The_secretshelper retrieves values at invocation time, does not log the value, fails closed (errors are not cached and degrade to the unauthenticated path), and caches for a short, env-tunable TTL (SECRET_CACHE_TTL_SECONDS, default 600s). Enable automatic rotation viaaws secretsmanager rotate-secretfor production. -
Least-privilege IAM -- Each component has scoped IAM policies:
- Lambda:
secret.grant_read(...)per secret (exact ARNs, pluskms:Decrypton the CMK). The credential-consuming tools run in this Lambda, so it is the only role with secret access. - AgentCore Runtime:
bedrock:InvokeModelandbedrock:InvokeModelWithResponseStreamfor the configured Claude profiles, explicit DynamoDB read/write actions plusdynamodb:TransactWriteItemson the leads/frontier tables, scoped Memory read/write, and inline Bedrock Guardrails through its Converse model calls. Converse still requiresbedrock:ApplyGuardrail, scoped to the exact CDK-created guardrail ARN; it does not make a separateApplyGuardrailAPI call. The Runtime role has no Secrets Manager access (it reads no secrets). - API Gateway: IAM authorization required on all methods.
- Lambda:
-
Encryption at rest -- The DynamoDB leads and frontier tables AND the four Secrets Manager secrets are encrypted with a single customer-managed KMS key (CMK) with key rotation enabled. CDK asset S3 buckets use SSE-S3 by default.
-
Encryption in transit -- All AWS SDK calls use HTTPS/TLS 1.2+ by default. External API calls via
httpxuse HTTPS with certificate validation enabled (verify=Trueis the default). The shared_http.get_with_retry()helper enforces HTTPS for all outbound requests. -
Network security -- AgentCore Runtime is configured for public network access to reach the AgentCore Gateway; the Lambda tool handlers make the external API calls. For deployments requiring network isolation, use VPC configuration with NAT gateways:
network_configuration=agentcore.RuntimeNetworkConfiguration.using_vpc(...). -
Input validation -- All tool handlers validate and sanitize inputs (length limits, allowed character sets, enum validation). User prompts are passed to the LLM but not interpolated into code or queries.
The table below maps the primary threats for this sample to in-repo controls. Several controls are configurable through environment variables; set the email review and grounding options to match your production policy.
| ID | Threat | Severity | Mitigation in this repo |
|---|---|---|---|
| TS001 | Prompt injection to the Bedrock agent | High | SAFETY_FENCE untrusted-data framing on all four agents; Bedrock Guardrail (GUARDRAIL_ID/GUARDRAIL_VERSION) with an InstructionOverride deny-topic. The PROMPT_ATTACK content filter is intentionally not used: it blocked benign multi-step task prompts and the agents' own injection fence; the deny-topic blocks real jailbreaks without that false-positive |
| TS002 | IAM privilege escalation via wildcards | High | Scoped IAM (inference-profile ARN, table ARNs, secret ARNs); scoped AgentCore Memory grant (read/write, no delete); CDK-nag suppressions documented |
| TS003 | SSRF to the AWS metadata endpoint | High | _http.py outbound host allow-list rejects any host outside the nine source-API domains, including 169.254.169.254. Residual: the allow-list matches hostnames, not resolved IPs, so DNS rebinding remains a known risk |
| TS004 | Rotated-secret reuse via cache TTL | High | CMK-encrypted secrets; grant_read per-secret (exact ARN + kms:Decrypt), no ListSecrets; Lambda-only access (Runtime has none); cache TTL 10 min, env-tunable via SECRET_CACHE_TTL_SECONDS; fail-closed (errors not cached) |
| TS005 | Unsolicited email without review | Medium | Optional human-in-the-loop (EMAIL_APPROVAL_REQUIRED=true stores leads as pending_review); compliance footer (COMPLIANCE_FOOTER_REQUIRED) |
| TS006 | Data poisoning from external APIs | Medium | Tool output treated as untrusted data; email persistence requires at least two distinct supported sources, grounding_gate.py verifies email claims against evidence, and GROUNDING_MIN_SCORE blocks low-grounding leads |
| TS007 | Frontier dedup race condition | Medium | claim_url uses a DynamoDB conditional write (attribute_not_exists); store_lead uses one DynamoDB transaction to reserve stable product-name and prospect-ID markers with the lead record |
| TS008 | Guardrail bypass outside the CDK deployment | Medium | The CDK deployment creates a guardrail and injects its ID/version into the Runtime. A standalone deployment must set both variables; add CloudWatch alarms on guardrail filter hits |
| TS009 | DynamoDB scan/query abuse | Medium | Product-name and recency lookups use GSIs, not scans; input limits bound query requests and the runtime role grants only the table APIs required for reads and writes |
| TS010 | API Gateway throttling bypass | Low | Throttling (50 req/s, 100 burst); add AWS WAF on API Gateway for distributed abuse |
| TS011 | Lambda timeout / cost abuse | Low | Lambda 60s timeout; AgentCore lifecycle limits (15 min idle, 8 hr max); node/execution timeouts in the orchestrator; bounded transient-model retry and per-agent tool-call budgets. Bedrock enforces a per-second ApplyGuardrail quota that every guardrail-inline Converse call consumes; drive concurrent runs within that quota or request an increase |
| TS012 | Credential exposure via CloudWatch | Low | Tool handlers return upstream_error (no str(e)); skip-list logs counts only; secret values not logged |
| TS013 | Sensitive prompt or evidence exposure in observability data | Medium | Strands and AgentCore telemetry is limited to CloudWatch IAM principals; do not put credentials or sensitive personal data in prompts, evidence, or tool results; application and usage log retention is 90 days |
| Level | Data Types | Storage | Controls |
|---|---|---|---|
| RESTRICTED | API keys, authentication tokens | AWS Secrets Manager | Customer-managed KMS key, scoped grant_read IAM, rotation-ready |
| CONFIDENTIAL | Prospect data, email drafts, scores | Amazon DynamoDB | Customer-managed KMS key, TTL expiry, scoped IAM |
| CONFIDENTIAL | Prompts, source evidence, agent telemetry | Amazon CloudWatch Logs / AgentCore spans | IAM access, Runtime application and usage log retention (90 days), CloudWatch Transaction Search |
| PUBLIC | Tool schemas | Source code repository | Version control, code review |
- AWS Lambda: Least-privilege execution role, ARM64 runtime, 60s timeout, 512 MB memory cap, dedicated CloudWatch log group with one-month retention.
- Amazon API Gateway: IAM authentication on all methods, request validation enabled, throttling configured, access logging to CloudWatch.
- Amazon Bedrock AgentCore Gateway: IAM inbound authorization, MCP protocol for tool discovery, Lambda target integration.
- Amazon Bedrock AgentCore Runtime: IAM authorization, public network (see
network security above), lifecycle limits, scoped Bedrock model access,
tracing_enabled, and application/usage log delivery. Strands emits OpenTelemetry telemetry natively and AgentCore Runtime exports it; CloudWatch Transaction Search is an account-level prerequisite for span search. - Amazon DynamoDB: Customer-managed KMS key encryption, TTL expiry, scoped
IAM policies (no
dynamodb:*wildcards), table-level resource ARN restrictions. - AWS Secrets Manager: CDK-created CMK-encrypted secrets, per-secret
grant_read(exact ARN +kms:Decrypt), noListSecretsor admin actions, Lambda-only access (Runtime role has none). - Amazon CloudWatch Logs: Lambda and API access-log groups use a one-month retention policy; Runtime application and usage groups use a 90-day retention policy. Access to AgentCore and Strands telemetry must be treated as access to confidential prompt and evidence data.
AWS responsibilities:
- Securing Amazon Bedrock AgentCore Runtime infrastructure and model execution environment
- Protecting AWS Lambda execution environment and function isolation
- Encrypting data in transit between services (AgentCore Gateway to Lambda)
- Managing IAM service-linked roles and service control policies
- Physical security of data centers and hardware
Customer responsibilities:
- Configuring least-privilege IAM policies for all components
- Managing AWS credentials securely (use IAM roles, not long-term keys)
- Rotating API keys in AWS Secrets Manager
- Validating and sanitizing inputs to LLM prompts
- Monitoring CloudWatch Logs and CloudTrail for security events
- Keeping dependencies updated and running security scans
- Reviewing CDK-nag findings before deployment
Run these scans before deployment:
# Python SAST
pip install bandit
bandit -r src/ infra/lambda/ -c pyproject.toml
# Dependency audit: install with the infra extra so CDK dependencies are included
pip install pip-audit
pip-audit
# CDK security checks (runs automatically during cdk synth)
cd infra
cdk synth # cdk-nag is configured in the CDK app
cd ..
# Semgrep (optional)
semgrep --config auto src/ infra/- IAM scope: CDK L2 constructs for AgentCore may generate broader permissions than strictly necessary. Mitigated by CDK-nag suppressions with documented justifications.
- External API dependencies: Tool handlers call third-party APIs that could change or return unexpected data. Mitigated by input validation, timeout limits, and error handling.
- Data residency: Amazon Bedrock global inference profiles may route requests to multiple AWS Regions. Restrict to a single Region if required.
- Third-party API terms: Each external API (Hacker News, YouTube, Reddit, etc.) has its own terms of service. Review and comply with rate limits, attribution requirements, and data usage restrictions before production use.
- Email compliance: Generated outreach emails should include unsubscribe mechanisms, physical mailing address, and clear sender identification per CAN-SPAM Act requirements before sending via Amazon SES.
The third-party credentials currently live in CMK-encrypted Secrets Manager and
are read by the Lambda tool handlers. The managed alternative is Amazon Bedrock
AgentCore Identity, which provides a token vault plus @requires_api_key and
@requires_access_token decorators that inject credentials at runtime without the
values ever entering application logs or LLM context. The Reddit OAuth2
client-credentials flow (hand-rolled in tools/reddit.py) is the strongest
candidate, since Identity's M2M OAuth2 provider would replace the manual token
fetch and caching entirely.
This migration is intentionally deferred, not skipped, for one architectural
reason: the Identity decorators resolve credentials from the AgentCore Runtime's
workload-identity context, but the credential-consuming tools run in the Lambda
behind the Gateway, which has no workload context. Adopting Identity therefore
requires either moving those tools into the Runtime (changing the documented
Gateway/Lambda topology) or fetching a workload token explicitly in the Lambda.
Until then, the CMK-encrypted, least-privilege, fail-closed Secrets Manager path
is the hardened baseline. To migrate: create credential providers with
agentcore add credential (never the MCP create/update tools, which would route
secrets through an LLM), reference them in agentcore.json, and decorate the
runtime-side tool functions.