Skip to content

fix(lambda): close the remaining contract validation gaps - #7241

Merged
waleedlatif1 merged 6 commits into
stagingfrom
fix/lambda-contract-bounds
Aug 29, 2026
Merged

fix(lambda): close the remaining contract validation gaps#7241
waleedlatif1 merged 6 commits into
stagingfrom
fix/lambda-contract-bounds

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Closes the Lambda contract validation gaps raised in review on the v0.8.16 release PR (v0.8.16: aws lambda, dynamics 365, actorless run fixes, usage chart #7224), following feat(lambda): add AWS Lambda integration with 50 operations #7216
  • Fixes two real holes in code-source selection: hasS3 required both S3 fields, so a partial pair alongside imageUri read as "image only" and the stray field still reached AWS; and an imageUri with no packageType was accepted even though it then defaults to Zip
  • Fixes the VPC guard, which only checked that both lists were supplied — supplying both with one empty passed and produced a partial update
  • Adds the documented AWS bounds for alias names (1–128 plus the pattern that excludes all-digit names), descriptions (256), layer names (140 plus the name-or-ARN pattern), optional functionName on the event source mapping operations (1–256), and RemovePermission statement IDs (1–100 plus its own pattern, which allows a dot where AddPermission does not)
  • Rejects structurally meaningless values: empty tag keys, empty Kafka bootstrap servers, more than one weighted routing entry, and a mapping supplying both an event source ARN and self-managed Kafka bootstrap servers

Every bound here is one the AWS Lambda API reference documents. Two review findings asked for constraints the reference does not state and were left alone — see the PR discussion for the reasoning.

Type of Change

  • Bug fix

Testing

  • 13 new boundary tests, 181 passing across the Lambda block, contracts, tools, and operations
  • bun run type-check clean; all 39 check:audits pass, plus docs-manifest:check and the block-registry check
  • 7,035 tests pass across the blocks, tools, and contracts suites
  • Verified every added bound against the AWS Lambda API reference rather than inferring from sibling operations

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Follow-up to #7216, from review on the release PR. Every bound added here is
one the AWS Lambda API reference documents; findings that asked for undocumented
limits were left alone.

Code source selection had two holes. `hasS3` required both S3 fields, so a
partial pair alongside `imageUri` read as "image only" and the stray S3 field
still went to AWS; and an `imageUri` with no `packageType` was accepted even
though the package type then defaults to Zip. Both now fail at the boundary,
naming the field to change, and the zip-only `s3ObjectVersion` and
`sourceKmsKeyArn` count as S3 fields for the exclusivity check.

The VPC guard only checked that both lists were supplied, so supplying both
with one empty passed and produced a partial update. Both must now be empty
(detach) or both populated (attach).

Documented bounds added:

- alias names: 1-128 and the documented pattern, which excludes all-digit names
- descriptions: 256 characters
- layer names: 140 characters and the name-or-ARN pattern
- optional `functionName` on the event source mapping operations: 1-256
- `RemovePermission` statement IDs: 1-100 and its own pattern, which allows a
  dot where `AddPermission` does not

Also rejects values that are structurally meaningless rather than merely
out of range: empty tag keys, empty Kafka bootstrap servers, more than one
weighted routing entry, and an event source mapping that supplies both an
event source ARN and self-managed Kafka bootstrap servers.
@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Aug 29, 2026 12:34am

Request Review

@greptile-apps

greptile-apps Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR tightens AWS Lambda request validation and aligns response projections with nullable or partially returned AWS fields.

  • Enforces documented bounds and structural constraints for aliases, layers, permissions, tags, code sources, VPC settings, and event-source mappings.
  • Extends Lambda response projections, including partial tag-read errors and self-managed Kafka bootstrap servers.
  • Adds boundary and regression coverage for the updated contracts.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported layer ARN validation defect is corrected at current HEAD.

Important Files Changed

Filename Overview
apps/sim/lib/api/contracts/tools/aws/lambda-get-layer-version.ts The corrected layer-name regex accepts bare names and layer ARNs containing twelve-digit AWS account IDs.
apps/sim/lib/api/contracts/tools/aws/lambda-list-layer-versions.ts The layer-listing contract uses the same corrected ARN validation as the get-layer-version contract.
apps/sim/lib/api/contracts/tools/aws/lambda-create-function.ts Code-source and VPC validation now rejects mixed, incomplete, and structurally inconsistent inputs.
apps/sim/lib/api/contracts/tools/aws/lambda-shared.ts Shared Lambda response schemas now reflect nullable AWS fields and additional projected event-source data.
apps/sim/lib/internal/lambda/execute-tool.test.ts Regression tests cover valid layer-name forms and reject ARNs without a twelve-digit account segment.

Reviews (5): Last reviewed commit: "test(lambda): prove every projection mat..." | Re-trigger Greptile

Comment thread apps/sim/lib/api/contracts/tools/aws/lambda-get-layer-version.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 16 files

Confidence score: 3/5

  • In apps/sim/lib/api/contracts/tools/aws/lambda-get-layer-version.ts, the ARN regex rejects valid 12-digit account IDs, preventing documented get-layer-version requests from succeeding; replace the literal d{12} with \d{12}.
  • In apps/sim/lib/api/contracts/tools/aws/lambda-create-function.ts, an empty optional S3 field can pass validation alongside imageUri but still be forwarded to the create operation, causing inconsistent source selection; reject the field when present or reject empty values.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/lib/api/contracts/tools/aws/lambda-get-layer-version.ts">

<violation number="1" location="apps/sim/lib/api/contracts/tools/aws/lambda-get-layer-version.ts:20">
P1: Valid layer ARN inputs are rejected because this regex uses literal `d{12}` instead of the digit escape. Replace it with `\d{12}` so `get-layer-version` accepts the ARN form documented by the contract.</violation>
</file>

<file name="apps/sim/lib/api/contracts/tools/aws/lambda-create-function.ts">

<violation number="1" location="apps/sim/lib/api/contracts/tools/aws/lambda-create-function.ts:52">
P2: When `imageUri` is accompanied by an optional S3 field set to `''`, this truthiness check treats the field as absent. The create operation still forwards defined empty source fields, so reject field presence or empty strings before sending the mixed code source to AWS.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/lib/api/contracts/tools/aws/lambda-get-layer-version.ts Outdated
Comment thread apps/sim/lib/api/contracts/tools/aws/lambda-create-function.ts
The account-ID segment was written as `\d{12}` inside a template literal, so the
emitted regex carried a literal `d{12}` and rejected every real layer ARN. The
existing test only covered an over-long name, which is why it passed.

Escapes the backslash and adds the coverage that would have caught it: a real
layer ARN and a bare layer name are both accepted, and an ARN whose account
segment is not twelve digits is rejected.
The mutual-exclusivity check used truthiness, so `imageUri` alongside
`s3Bucket: ''` read as image-only while the create operation still forwarded the
defined empty field to AWS.

An empty string is meaningless for every code-source field, so each is now
`.min(1)` at the contract rather than special-cased in the refinement.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile-apps

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

@cubic review

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 16 files

Confidence score: 3/5

  • In apps/sim/lib/api/contracts/tools/aws/lambda-create-event-source-mapping.ts, an empty eventSourceArn can pass validation when bootstrap servers are provided, causing the executor to send both EventSourceArn: '' and SelfManagedEventSource to AWS; validate the field explicitly and add coverage for the empty-string case.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/lib/api/contracts/tools/aws/lambda-create-event-source-mapping.ts">

<violation number="1" location="apps/sim/lib/api/contracts/tools/aws/lambda-create-event-source-mapping.ts:70">
P2: When `eventSourceArn` is an empty string and bootstrap servers are present, this truthiness check accepts the request and the executor sends both `EventSourceArn: ''` and `SelfManagedEventSource` to AWS. Check field presence (or reject empty `eventSourceArn`) so malformed requests fail contract validation instead of reaching AWS.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

An empty `eventSourceArn` alongside bootstrap servers slipped past the
mutual-exclusivity check for the same reason the code-source fields did: the
guard tests truthiness, so a defined-but-empty value reads as absent while the
operation still forwards it.

Rather than patch each field as it surfaces, every optional string field now
rejects an empty value. The tool layer already drops `''` before it reaches a
contract, so an empty value can only arrive from a malformed direct call, and
forwarding it to AWS is never right.

`description` is exempt: AWS documents it as "Minimum length of 0", so an empty
value legitimately clears it. Both behaviours are covered by tests.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile-apps

A comprehensive validation pass against the API reference found the previous
commit's blanket "no empty optional strings" rule was wrong. Several Lambda
parameters document an empty string as meaningful, and their patterns say so:
KMSKeyArn, SourceKMSKeyArn, and DeadLetterConfig.TargetArn all carry
`(arn:...)|()`, whose trailing alternative matches the empty string, and the
on-success/on-failure destinations document `Minimum length of 0` with a
pattern beginning `$|`. For each, empty is how the setting is cleared.

The rule is now opt-in rather than opt-out: only the five fields feeding a
truthiness-based cross-field check reject an empty value. That removes 47
constraints and leaves the ones that were actually reported.

Also from the same pass:

- Supplying an image URI no longer demands an explicit `packageType`. That
  subBlock is advanced with no default, so requiring it produced a 400 naming
  a control the user cannot see; the operation derives Image from the code
  source instead, and only an explicit Zip alongside an image is rejected.
- `fileSystemConfigs` was the one projection without a null guard, so an
  omitted field vanished from the block output rather than reading null.
- An absent function URL now maps to null instead of an empty string a
  workflow could build a request against.
- GetFunction reports `tagsError`, so a partial tag-read failure is
  distinguishable from a function with no tags, and marks `configuration`
  nullable to match what the operation returns.
- Event source mappings report `selfManagedKafkaBootstrapServers`, which
  could be set but never read back.
- TagResource rejects an empty tag map instead of reporting "0 tags applied".
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

Ran a comprehensive validation pass over all 50 operations against the AWS Lambda API reference, and it found a regression I had introduced in cc9821ffd6 — one that both reviewers scored 5/5 over. Fixed in 0418829006.

The regression. That commit blanket-applied .min(1) to every optional string field. That premise is wrong for Lambda: several parameters document an empty string as meaningful.

  • KMSKeyArn, SourceKMSKeyArn, DeadLetterConfig.TargetArn — pattern (arn:...)|(), whose trailing alternative matches ""
  • OnSuccess.Destination / OnFailure.DestinationMinimum length of 0, pattern begins $|

For each of those, empty is how you clear the setting. 14 call sites across 4 parameter types, plus three more where the reference states Minimum length of 0 (handler, sourceAccount, eventSourceToken).

The rule is now opt-in instead of opt-out: only the five fields feeding a truthiness-based cross-field check reject an empty value. Net 47 constraints removed, 0 added, with a regression test pinning that the clearable fields still accept "" — verified by reintroducing the bug and watching it fail.

My mistake was reaching for a sweeping default under review pressure after the same defect shape came up three times. A blanket rule applied to fields nobody examined is how you regress silently.

Also fixed from the same pass, one of which was a real UX break:

  • Creating a container-image function was unreachable from the default view. packageType is an advanced subBlock with no default, but requiring an explicit Image alongside imageUri produced a 400 naming a control the user cannot see. The operation now derives Image from the code source; only an explicit Zip alongside an image is rejected.
  • fileSystemConfigs was the single projection without a null guard, so an omitted field vanished from the output instead of reading null.
  • An absent function URL mapped to "" rather than null — a workflow interpolating it would have built a request against an empty URL instead of failing loudly.
  • GetFunction now reports tagsError, so a partial tag-read failure is distinguishable from a function that genuinely has no tags, and configuration is marked nullable to match what the operation returns.
  • Event source mappings now report selfManagedKafkaBootstrapServers, which could be set but never read back.
  • TagResource rejects an empty tag map instead of reporting "0 tags applied" as a success.

Two findings I deliberately did not act on:

  • runtime / compatibleRuntime as free strings rather than an enum. AWS adds runtimes regularly; pinning the list locally would reject newly released ones until someone updated the file. That is the same over-strict failure mode as the regression above.
  • functionName capped at 256 everywhere, where the reference documents 140 on about twenty operations. This under-validates rather than over-validates, so the cost is an error arriving from AWS instead of locally — worth less than the regression risk of getting the per-operation split wrong.

The pass confirmed the rest is sound: all 50 response projections match their contracts key-for-key, every SDK request field name is valid, the block/tool/contract three-way alignment has zero drift across all 50 operations, and every remaining numeric range, pattern, and enum matches the documented values.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile-apps

Reading code confirmed the projections and schemas agree, but nothing ran them
against each other. This runs all eight shared mappers against their schemas in
both directions: every declared key is emitted and non-undefined, no undeclared
key is emitted, and the result parses — for an empty AWS response, which is the
common case, and for a fully-populated one.

Verified the suite fails when either defect class is reintroduced: a mapper that
stops emitting a declared key, and a projection that leaks `undefined` where the
schema declares a value.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile-apps

@waleedlatif1
waleedlatif1 merged commit 4aa1c04 into staging Aug 29, 2026
26 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/lambda-contract-bounds branch August 29, 2026 00:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant