Skip to main content

Catch AWS Lambda IAM permission errors and event-shape bugs before you deploy — a local pre-flight linter and AWS-faithful runtime. No Docker, no SAM, no invocation charges.

Project description

lambda-preflight

Catch AWS Lambda IAM permission errors and event-shape bugs before you deploy.

Every "local lambda" tool runs your handler. lambda-preflight does something none of them do: it checks your handler's AWS calls against your execution role's real IAM policies and fails locally — so you find the missing s3:PutObject on your laptop, not in a CloudWatch log after a failed deploy.

No Docker. No SAM CLI. No cloud invocations, so no invocation charges.

pip install "lambda-preflight[aws]"

The headline: local IAM linting

Your Lambda runs as an execution role. When that role is missing a permission, you normally find out the slow way — deploy, invoke, read the AccessDenied in the logs, redeploy. lambda-preflight pulls your role's real policies once, caches them, and checks every AWS call your handler makes before anything leaves your machine.

from lambda_preflight import pull_role, PolicyEvaluator, intercept_boto3
from lambda_preflight import LambdaHarness, events

# Pull the execution role's real policies (managed + inline + boundary), cached after first call.
policies  = pull_role("my-function-exec-role")
evaluator = PolicyEvaluator(policies, strict=True)

harness = LambdaHarness("app.handler", timeout=30)

# boto3 calls inside the handler are authorized against the real policy — locally.
with intercept_boto3(evaluator):
    result = harness.invoke(events.sqs(body={"orderId": 123}))
    # If app.handler calls s3:PutObject and the role can't → AccessDenied raised here,
    # with the same reason AWS would give, before you ever deploy.

Three ways to load policies, so it works on a plane or in locked-down accounts:

from lambda_preflight import pull_role, load_role_policies, PolicySet

pull_role("my-exec-role")                       # 1. live pull  — needs creds + IAM read, caches result
load_role_policies("my-exec-role")              # 2. offline    — reuse cache, no AWS call, no charge
PolicySet.from_policy_documents([{"Statement": [...]}])  # 3. BYO JSON — when you can't pull

It models the parts that actually decide real permissions: explicit-deny-wins, implicit deny, wildcard actions, resource matching, permission-boundary intersection, and common condition operators. What it can't faithfully evaluate (uncommon condition keys, SCPs, resource-based policies) it flags — in strict=True mode it raises CannotEvaluate rather than returning a false green. It tells you the edge of its own knowledge instead of guessing.


The foundation: an AWS-faithful local runtime

The linter needs something real to run against, so the runtime half is built to match AWS's actual invocation contract — not a stubbed approximation.

Schema-accurate event fixtures

The #1 cause of "worked locally, broke in AWS" is a test event whose shape is wrong. These builders emit payloads matching AWS's real schemas, so what you test is what you'll get. REST (v1) and HTTP (v2) API Gateway differ meaningfully — they're separate on purpose:

events.api_gateway_v1(method="GET", path="/health")
events.api_gateway_v2(method="POST", path="/orders", body={"id": 1})
events.sqs(records=[{"a": 1}, {"b": 2}])        # real batch shape, messageAttributes, md5
events.sns(message={"x": 1}, subject="hi")
events.s3(bucket="b", key="k.txt", event_name="ObjectCreated:Put")
events.eventbridge(detail={"k": "v"}, detail_type="my.type", source="svc")
events.dynamodb_stream(keys={"id": {"S": "1"}}, event_name="INSERT")

Faithful invocation semantics

result = harness.invoke(event)
result.ok          # success vs. error
result.payload     # what the handler returned
result.error       # {errorType, errorMessage, stackTrace} — AWS's exact envelope
result.cold_start  # models warm-container reuse across invokes

The context reproduces real fields (aws_request_id, invoked_function_arn, log names) and a live get_remaining_time_in_millis() countdown. Timeouts are enforced the way AWS enforces them. Lambda's environment variables are injected so handler code that reads them behaves the same.

pytest, because CI is the point

Fixtures register automatically — fast, cloud-free, zero-charge tests:

def test_orders(lambda_harness, lambda_events):
    harness = lambda_harness("app.handler", timeout=5)
    result = harness.invoke(lambda_events.sqs(body={"order": 42}))
    assert result.ok

How this differs from other local-lambda tools

Tools like python-lambda-local and local-aws-lambda are runners — they execute your handler against an event dict you supply. That's the commodity half, and it's solved. lambda-preflight includes a competent runner too, but its reason to exist is what sits on top:

Typical local-lambda tools lambda-preflight
Run a handler locally
Schema-accurate event fixtures ✋ you write the dict ✅ built-in, v1 ≠ v2
Faithful context / timeout / cold-start / AWS-shape errors partial
IAM permission linting against your real role unique

Honest scope

This kills the shape / contract / runtime-semantics / identity-permission class of "works locally, breaks in AWS" failures — which is most of the day-to-day pain. It does not, and a local tool cannot, reproduce cloud-only realities: cold-start latency, live concurrency, account quotas, SCPs, and resource-based policies (the last two are recorded in PolicySet.unscoped, never silently ignored). The IAM layer is a pre-flight linter, not a parity guarantee. For final confidence, run against real AWS credentials.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lambda_preflight-0.1.1.tar.gz (28.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lambda_preflight-0.1.1-py3-none-any.whl (25.8 kB view details)

Uploaded Python 3

File details

Details for the file lambda_preflight-0.1.1.tar.gz.

File metadata

  • Download URL: lambda_preflight-0.1.1.tar.gz
  • Upload date:
  • Size: 28.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.0

File hashes

Hashes for lambda_preflight-0.1.1.tar.gz
Algorithm Hash digest
SHA256 953f78ad37ce0bd797a1a7dda762e3dae7614316aa73a6941380ce8541262481
MD5 5115b93c51a88feae059655fb8ca61eb
BLAKE2b-256 73a031c629032b31063771f1108bea5163ef39b0df8b8ee0b894810cdb3c38ea

See more details on using hashes here.

File details

Details for the file lambda_preflight-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for lambda_preflight-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c93062b9caef774a48e8694c0bca47ea9196cca2a0ac896c42dd9016e204ead5
MD5 a716bdab3dcc84942bd7ec9b5cd8faff
BLAKE2b-256 8fc5c6c4c1dc35514a4b1b046ab3cf2a5a504b7b14061686451d6685361d83d6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page