Last updated on July 13, 2026
Part 2: Prepare Claude Haiku 4.5 in Amazon Bedrock
Current Amazon Bedrock documentation states that AWS enables foundation-model access by default. However, first-time users of Anthropic models may still need to submit use-case details before invocation.
To complete this step, select an Anthropic model in the Bedrock model catalog or playground.
In the AWS Management Console:
- Open Amazon Bedrock in
us-east-1. - Open the model catalog or model playground.
- Select Claude Haiku 4.5.
- Submit the Anthropic use-case details if prompted.
- Run a short test message.
Next, verify programmatic invocation with the AWS CLI:
aws bedrock-runtime converse
--model-id "anthropic.claude-haiku-4-5-20251001-v1:0"
--messages '[{"role":"user","content":[{"text":"Find the security issue: password = "admin123""}]}]'
--region us-east-1
The response should contain a model-generated message and token-usage metadata.
The final application uses the following US geographical inference profile:
us.anthropic.claude-haiku-4-5-20251001-v1:0
AWS documents this identifier as a supported geo-inference ID for Claude Haiku 4.5.
Part 3: Create the GitHub Repository and Token
Create a public GitHub repository named Diffender with:
- A
README.md - An MIT license
- A Python
.gitignore
Â
Next, create a fine-grained personal access token restricted to the Diffender repository.
Grant these repository permissions:
- Pull requests: Read and write
- Issues: Read and write
- Contents: Read-only
The current implementation retrieves the pull request through the pull requests endpoint and posts the final message through the issue-comments endpoint.
That distinction is easy to miss. GitHub pull requests are also issues internally, so creating a general conversation comment on a pull request uses:
POST /repos/{owner}/{repo}/issues/{issue_number}/comments
GitHub requires pull request or contents read access to retrieve the pull request. In addition, GitHub evaluates fine-grained token permissions for each REST endpoint.
Copy the token beginning with:
github_pat_
Do not add it to the repository, a local configuration file, or the SAM template.
Part 4: Create the Project Structure
Clone the repository and create the application directories:
git clone https://github.com/chescaasignacion/Diffender.git
cd Diffender
mkdir -p src test-samples
touch src/requirements.txt
The project structure is:
Diffender/
├── src/
│ ├── app.py
│ └── requirements.txt
├── test-samples/
│ └── insecure_example.py
├── template.yaml
├── README.md
├── LICENSE
└── .gitignore
AWS SAM Template
Create template.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Diffender - an AI security code reviewer for GitHub PRs, powered by Amazon Bedrock.
Parameters:
BedrockModelId:
Type: String
Default: us.anthropic.claude-haiku-4-5-20251001-v1:0
Globals:
Function:
Timeout: 60
MemorySize: 256
Runtime: python3.12
Resources:
DiffenderFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: diffender
Handler: app.lambda_handler
CodeUri: src/
Environment:
Variables:
BEDROCK_MODEL_ID: !Ref BedrockModelId
GITHUB_TOKEN_PARAM: /diffender/github-token
WEBHOOK_SECRET_PARAM: /diffender/webhook-secret
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ssm:GetParameter
Resource:
- !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/diffender/*
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- kms:Decrypt
Resource: ''
Condition:
StringEquals:
kms:ViaService: !Sub ssm.${AWS::Region}.amazonaws.com
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- bedrock:InvokeModel
Resource: ''
Events:
Webhook:
Type: Api
Properties:
Path: /webhook
Method: post
Outputs:
WebhookUrl:
Value: !Sub https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/webhook
The template defines one Lambda function and one API Gateway REST API route. It also creates the Lambda execution role from inline policy statements.
The function can perform three categories of AWS action:
- Retrieve the Parameter Store values
- Decrypt encrypted parameters through AWS KMS
- Invoke Amazon Bedrock
The Amazon Bedrock and AWS KMS statements use wildcard resource values in this tutorial. Therefore, a production version should evaluate whether those resources can be narrowed based on the selected AWS KMS key, model, and inference-profile ARNs.
Lambda Handler
The complete Lambda handler is available in the <a href=”https://github.com/chescaasignacion/Diffender”>Diffender GitHub repository</a>. The following sections focus on the code that controls secret retrieval, signature validation, Amazon Bedrock invocation, and GitHub integration.
Retrieve Secrets from Parameter Store
ssm = boto3.client("ssm")
_cache = {}
def _get_param(name):
if name not in _cache:
_cache[name] = ssm.get_parameter(
Name=name,
WithDecryption=True,
)["Parameter"]["Value"]
return _cache[name]
The function retrieves each parameter only once per warm Lambda execution environment. Later invocations can reuse the cached value.
Validate the GitHub Signature
def verify_signature(raw_body, signature_header):
if not signature_header:
return False
secret = _get_param(WEBHOOK_SECRET_PARAM).encode()
expected = (
"sha256="
+ hmac.new(
secret,
raw_body,
hashlib.sha256
).hexdigest()
)
return hmac.compare_digest(
expected,
signature_header
)
This function recalculates the HMAC-SHA-256 signature from the raw request body and compares it with the value supplied by GitHub.
Retrieve the Pull Request Diff
def get_pr_diff(owner, repo, number):
url = (
f"https://api.github.com/repos/"
f"{owner}/{repo}/pulls/{number}"
)
return _github_request(
"GET",
url,
accept="application/vnd.github.v3.diff",
)
The GitHub API returns the code changes as a unified diff, which contains the lines added, removed, or modified by the pull request.
Invoke Claude Through Amazon Bedrock
The function limits the diff to 60,000 characters before sending it to the model. It also caps the response at 1,500 tokens.
Post the Review to GitHub
GitHub uses the Issues Comments API for general pull request conversation comments.
Requirements File
The tutorial does not install an external application package. The Python Lambda runtime includes the AWS SDK for Python, so src/requirements.txt can remain empty for this build.
For a controlled production release, package and pin the required SDK version so the application does not depend on whichever SDK version the runtime includes.
Deliberately Insecure Test File
Create test-samples/insecure_example.py:
This intentionally unsafe file belongs only in an isolated demonstration repository. Therefore, never use it in an actual application.
Part 5: Build and Deploy with AWS SAM
AWS SAM keeps the application code and infrastructure definition in the same project. Readers who need a refresher on SAM templates, resources, and deployment commands can review the AWS SAM cheat sheet.
Build the application in a Python 3.12-compatible container:
The sam build command prepares the source and infrastructure definition for deployment. In addition, using a container reduces differences between the local machine and the Lambda build environment.
Next, deploy the stack:
Use the following guided responses:
During deployment, SAM displays an authorization warning because the endpoint does not use an API Gateway authorizer. That is intentional because GitHub cannot authenticate through IAM or Amazon Cognito before sending the webhook.
Instead, Diffender verifies each request through application-level HMAC validation.
After deployment, copy the WebhookUrl from the CloudFormation outputs:
Finally, commit samconfig.toml only after confirming that it contains no secrets. The file normally stores deployment preferences and parameter values, but secrets should remain in Parameter Store.
Â













Next, generate and store a random webhook secret:




























