github ministackorg/ministack v1.4.17

3 hours ago

What's Changed

[1.4.17] — 2026-08-14

Added

  • CloudFormation — AWS::Lambda::LayerVersionPermission — a stack that grants layer access (serverless-python-requirements allowedAccounts, CDK addPermission) failed with Unsupported resource type and rolled back. The type now calls AddLayerVersionPermission, Ref returning <layer version ARN>#<statement id>. Layer policies also gained a real RevisionId (a stale one fails PreconditionFailedException), principal validation, and AWS's root-ARN statement shape. Contributed by @iot-rocket.
  • CloudWatch Logs — GetLogGroupFields — returns field names found in recent stored events with a rough presence percent (logGroupFields: [{name, percent}]). Honors logGroupName or logGroupIdentifier, optional time (±8 minutes) or the default last 15 minutes, system @* fields, and flattened JSON message keys. Contributed by @ovsteenb.
  • Step Functions — aws-sdk:route53 service integration — a Step Functions task calling the Route 53 SDK integration (e.g. ChangeResourceRecordSets) was unsupported. It now serializes the request through the rest-xml path and names an aws-sdk error the AWS way, Route53.<Error>Exception. Contributed by @bandle.
  • Step Functions — States.Base64Encode / States.Base64Decode intrinsics — the two intrinsic functions are implemented (UTF-8, the documented 10,000-character input cap, lenient decode padding); an over-cap or non-string argument fails the execution with States.Runtime. Contributed by @bandle.
  • CloudFormation — AWS::IoT::ThingType, AWS::IoT::Policy, and AWS::Cognito::IdentityPoolRoleAttachment — each type failed a stack with Unsupported resource type and rolled it back; they now provision onto their own service (create/delete), so a stack declaring an IoT thing type or policy, or attaching roles to a Cognito identity pool, deploys cleanly. Reported by @iot-rocket.

Fixed

  • API Gateway (HTTP API / v2) — custom Lambda (REQUEST) authorizers are now invoked and enforced_handle_execute_in_scope only branched on auth_type == "JWT", so a route with a CUSTOM authorization type (the type a route gets when it references a REQUEST authorizer) fell through unauthenticated: the authorizer Lambda was never invoked at all, regardless of whether the request carried a valid, invalid, or missing token. Adds a REQUEST-authorizer data-plane path mirroring the REST (v1) fix from 1.4.16 (_authorize_request_v1) — honors authorizerPayloadFormatVersion (1.0 IAM-policy-shaped event vs. 2.0), enableSimpleResponses ({isAuthorized, context}) vs. IAM policy ({principalId, policyDocument, context}) response formats, and authorizerResultTtlInSeconds caching — and populates requestContext.authorizer.lambda for the downstream integration. Reported against real-world usage; the earlier REST (v1) authorizer fix covered v1 only. Contributed by @ryan-bennett.
  • EventBridge — API destination delivery no longer leaks credentials, and caller-supplied outbound values are validated — API destination requests now validate every caller-controlled value before it reaches the wire. (1) Delivery and OAuth token requests no longer follow redirects, preventing credentials from reaching another host; 3xx responses are non-retryable failures. (2) InvocationEndpoint and OAuth AuthorizationEndpoint must be dialable http(s):// URLs. The outbound opener also rejects non-HTTP(S) schemes, while loopback/private/link-local hosts remain allowed for the local emulator. (3) HttpMethod and AuthorizationType are validated against their API enums. (4) ApiKeyName can no longer override reserved headers. (5) DeauthorizeConnection removes authorization parameters, and delivery skips connections that are not AUTHORIZED. (6) OAuth token responses are capped at 1 MiB. Contributed by @t-rech.
  • Lambda — uncaught handler exceptions report X-Amz-Function-Error: Unhandled — the Docker/RIE executor read the error class off a response header the RIE never sets, so every failure was reported Handled and API Gateway consumers keying off the 502-for-Unhandled contract never took the error path. Headerless payloads are now classified by shape: the runtime's error serialization reports Unhandled, an HTTP-style envelope with statusCode stays Handled. Contributed by @iot-rocket.
  • IoT — CreateThingType is idempotent for identical re-creates — re-creating an existing thing type always returned ResourceAlreadyExistsException, so a retried request or a re-run provisioning script failed where AWS succeeds. The same thingTypeProperties now return the existing ids — absent, null and empty compare equal, searchableAttributes is unordered — and only a real mismatch keeps the 409, as CreateThing already did. Contributed by @iot-rocket.
  • EC2 — RevokeSecurityGroupIngress / RevokeSecurityGroupEgress honour SecurityGroupRuleIds — both read only IpPermissions, so a revoke by rule id (as Terraform does) returned Return=true while removing nothing, leaving the rule in place forever. Ids now resolve against the group's sgr-* rules, which are removed with their tags and echoed in revokedSecurityGroupRuleSet; one unknown id rejects the whole call with InvalidSecurityGroupRuleId.NotFound. Contributed by @iot-rocket.
  • Cognito — /oauth2/userInfo returns custom attributes and the plain username — the response filtered attributes through a fixed standard-OIDC allowlist, dropping custom: attributes; it now returns them (as AWS does for openid and openid profile), plus the plain username claim alongside cognito:username, and middle_name. Contributed by @rjmackay.
  • CloudFormation — AWS::S3::Bucket NotificationConfiguration is applied instead of silently dropped — the bucket provisioner read only BucketName / VersioningConfiguration, so a NotificationConfiguration (native, or expanded from a SAM Events: S3 trigger) reached CREATE_COMPLETE with no binding and S3 → Lambda/SQS/SNS/EventBridge never fired. It is now routed through the same path PutBucketNotificationConfiguration takes (create, update, and clear-on-remove), translating the CloudFormation property names to the S3 API's. Reported by @VictorAlejMadrid.
  • CloudFormation — stack metadata survives a PERSIST_STATE=1 restart — a stop/restore kept every provisioned resource but ListStacks / DescribeStacks / ListExports came back empty, because the stack records, events, exports, and change sets were never persisted. They are now saved and restored with the rest of the state. Reported by @iot-rocket.
  • S3 — encoding-type=url leaves the forward slash intact — key names, CommonPrefixes, and the echoed Delimiter percent-encoded / as %2F, so a delimiter-collapsed "folder" listing was unreadable; / is now left alone while spaces and + are still encoded, matching S3. Reported by @gaul.
  • S3 — a delimited listing's NextMarker is the common prefix — when a page ended on a CommonPrefixes group, NextMarker pointed at an underlying key rather than the prefix, so a client resuming from it re-walked keys it had already been told about as a prefix. NextMarker is now the last row returned, and resuming skips the whole group. Reported by @gaul.
  • S3 — CopyObject honours the copy-source date preconditionsx-amz-copy-source-if-modified-since / -if-unmodified-since were ignored (only the ETag conditions applied). Both are now evaluated with AWS's documented precedence (if-match over if-unmodified-since, if-none-match over if-modified-since), returning 412 PreconditionFailed on a mismatch. Reported by @gaul.
  • S3 — a canned ACL expands to its group grantsx-amz-acl at PutObject, and a canned PutObjectAcl, stored only the owner's FULL_CONTROL, so public-read reported no public grant. Canned ACLs now expand to the grants they imply (public-read → AllUsers READ, public-read-writeREAD + WRITE, authenticated-read → AuthenticatedUsers READ), and an invalid canned value is rejected. Reported by @gaul.
  • S3 — a malformed Content-MD5 is InvalidDigest, not BadDigest — a Content-MD5 that was not valid base64 or did not decode to 16 bytes returned BadDigest, which AWS reserves for a well-formed digest that does not match the body; a malformed value now returns InvalidDigest. Reported by @gaul.
  • S3 — CompleteMultipartUpload's Location reflects the request host — the Location was built from a hard-coded http://localhost:4566, so it was wrong on any other port or host; it now echoes the endpoint the client reached. Reported by @gaul.
  • S3 — PutObject echoes the bucket's default encryption header — after PutBucketEncryption, a PutObject reply carried no x-amz-server-side-encryption, so a client could not confirm the object was encrypted as configured; the applied algorithm (AES256, or aws:kms with its key id) is now stamped on the reply. Reported by @gaul.

Don't miss a new ministack release

NewReleases is sending notifications on new releases.