tests/test.sh exit code (0 → resolved); the classification below is post-hoc and cannot change it.{
"command": "echo \"AWS_ENDPOINT_URL=$AWS_ENDPOINT_URL\"; echo \"ENFORCE_IAM=$ENFORCE_IAM\"; aws --version",
"description": "Check environment variables and AWS CLI version"
}AWS_ENDPOINT_URL=http://localstack:4566 ENFORCE_IAM= aws-cli/2.34.48 Python/3.14.4 Linux/6.10.14-linuxkit exe/aarch64.ubuntu.24 [stdout] AWS_ENDPOINT_URL=http://localstack:4566 ENFORCE_IAM= aws-cli/2.34.48 Python/3.14.4 Linux/6.10.14-linuxkit exe/aarch64.ubuntu.24
{
"command": "aws ssm get-parameters-by-path --path /harbor/httpapi/ --recursive --output json",
"description": "List all SSM parameters under /harbor/httpapi/"
}{
"Parameters": [
{
"Name": "/harbor/httpapi/api-id",
"Type": "String",
"Value": "9c815daf",
"Version": 1,
"LastModifiedDate": "2026-05-16T16:17:30.821000+00:00",
"ARN": "arn:aws:ssm:us-east-1:000000000000:parameter/harbor/httpapi/api-id",
"DataType": "text"
},
{
"Name": "/harbor/httpapi/user-pool-id",
"Type": "String",
"Value": "us-east-1_7981fafba8e94b82a5960d0b4f864c84",
"Version": 1,
"LastModifiedDate": "2026-05-16T16:17:31.603000+00:00",
"ARN": "arn:aws:ssm:us-east-1:000000000000:parameter/harbor/httpapi/user-pool-id",
"DataType": "text"
},
{
"Name": "/harbor/httpapi/app-client-id",
"Type": "String",
"Value": "pu0pmgrnf9omi6zjwvwpvcyjwh",
"Version": 1,
"LastModifiedDate": "2026-05-16T16:17:32.111000+00:00",
"ARN": "arn:aws:ssm:us-east-1:000000000000:parameter/harbor/httpapi/app-client-id",
"DataType": "text"
},
{
"Name": "/harbor/httpapi/authorizer-id",
"Type": "String",
"Value": "5a5a06e0",
"Version": 1,
"LastModifiedDate": "2026-05-16T16:17:32.576000+00:00",
"ARN": "arn:aws:ssm:us-east-1:000000000000:parameter/harbor/httpapi/authorizer-id",
"DataType": "text"
},
{
"Name": "/harbor/httpapi/integration-id",
"Type": "String",
"Value": "ab2b68d2",
"Version": 1,
"LastModifiedDate": "2026-05-16T16:17:33.032000+00:00",
"ARN": "arn:aws:ssm:us-east-1:000000000000:parameter/harbor/httpapi/integration-id",
"DataType": "text"
},
{
"Name": "/harbor/httpapi/function-name",
"Type": "String",
"Value": "harbor-api-handler",
"Version": 1,
"LastModifiedDate": "2026-05-16T16:17:33.481000+00:00",
"ARN": "arn:aws:ssm:us-east-1:000000000000:parameter/harbor/httpapi/function-name",
"DataType": "text"
},
{
"Name": "/harbor/httpapi/role-arn",
"Type": "String",
"Value": "arn:aws:iam::000000000000:role/harbor-api-handler-role",
"Version": 1,
"LastModifiedDate": "2026-05-16T16:17:34.004000+00:00",
"ARN": "arn:aws:ssm:us-east-1:000000000000:parameter/harbor/httpapi/role-arn",
"DataType": "text"
},
{
"Name": "/harbor/httpapi/resource-server-id",
"Type": "String",
"Value": "harbor-api",
"Version": 1,
"LastModifiedDate": "2026-05-16T16:17:34.442000+00:00",
"ARN": "arn:aws:ssm:us-east-1:000000000000:parameter/harbor/httpapi/resource-server-id",
"DataType": "text"
},
{
"Name": "/harbor/httpapi/user-pool-domain",
"Type": "String",
"Value": "harbor-api-domain",
"Version": 1,
"LastModifiedDate": "2026-05-16T16:17:34.895000+00:00",
"ARN": "arn:aws:ssm:us-east-1:000000000000:parameter/harbor/httpapi/user-pool-domain",
"DataType": "text"
},
{
"Name": "/harbor/httpapi/log-group-name",
"Type": "String",
"Value": "/aws/apigw/harbor-api",
"Version": 1,
"LastModifiedDate": "2026-05-16T16:17:35.664000+00:00",
"ARN": "arn:aws:ssm:us-east-1:000000000000:parameter/harbor/httpapi/log-group-name",
"DataType": "text"
},
{
"Name": "/harbor/httpapi/custom-domain-name",
"Type": "String",
"Value": "api.harbor.example",
"Version": 1,
"LastModifiedDate": "2026-05-16T16:17:36.282000+00:00",
"ARN": "arn:aws:ssm:us-ea
…[truncated]{
"command": "aws apigatewayv2 get-api --api-id 9c815daf --output json",
"description": "Inspect the API"
}{
"ApiEndpoint": "http://9c815daf.execute-api.localhost.localstack.cloud:4566",
"ApiId": "9c815daf",
"ApiKeySelectionExpression": "$request.header.x-api-key",
"CorsConfiguration": {
"AllowCredentials": false,
"AllowHeaders": [
"content-type"
],
"AllowMethods": [
"*"
],
"AllowOrigins": [
"*"
],
"MaxAge": 0
},
"CreatedDate": "2026-05-16T16:17:17.797000+00:00",
"DisableExecuteApiEndpoint": false,
"IpAddressType": "ipv4",
"Name": "harbor-api",
"ProtocolType": "HTTP",
"RouteSelectionExpression": "$request.method $request.path",
"Tags": {}
}
[stdout]
{
"ApiEndpoint": "http://9c815daf.execute-api.localhost.localstack.cloud:4566",
"ApiId": "9c815daf",
"ApiKeySelectionExpression": "$request.header.x-api-key",
"CorsConfiguration": {
"AllowCredentials": false,
"AllowHeaders": [
"content-type"
],
"AllowMethods": [
"*"
],
"AllowOrigins": [
"*"
],
"MaxAge": 0
},
"CreatedDate": "2026-05-16T16:17:17.797000+00:00",
"DisableExecuteApiEndpoint": false,
"IpAddressType": "ipv4",
"Name": "harbor-api",
"ProtocolType": "HTTP",
"RouteSelectionExpression": "$request.method $request.path",
"Tags": {}
}{
"command": "aws apigatewayv2 get-authorizers --api-id 9c815daf --output json",
"description": "Inspect authorizers"
}{
"Items": [
{
"AuthorizerId": "5a5a06e0",
"AuthorizerType": "JWT",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": [
"us-east-1_7981fafba8e94b82a5960d0b4f864c84"
],
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_6ccad0de3b6e4e1fbe7656167b3c9131"
},
"Name": "harbor-jwt-auth"
},
{
"AuthorizerId": "bc54ea96",
"AuthorizerType": "JWT",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": [
"pu0pmgrnf9omi6zjwvwpvcyjwh"
],
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_6ccad0de3b6e4e1fbe7656167b3c9131"
},
"Name": "harbor-jwt-orphan"
}
]
}
[stdout]
{
"Items": [
{
"AuthorizerId": "5a5a06e0",
"AuthorizerType": "JWT",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": [
"us-east-1_7981fafba8e94b82a5960d0b4f864c84"
],
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_6ccad0de3b6e4e1fbe7656167b3c9131"
},
"Name": "harbor-jwt-auth"
},
{
"AuthorizerId": "bc54ea96",
"AuthorizerType": "JWT",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": [
"pu0pmgrnf9omi6zjwvwpvcyjwh"
],
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_6ccad0de3b6e4e1fbe7656167b3c9131"
},
"Name": "harbor-jwt-orphan"
}
]
}{
"command": "aws apigatewayv2 get-integrations --api-id 9c815daf --output json",
"description": "Inspect integrations"
}{
"Items": [
{
"ConnectionType": "INTERNET",
"IntegrationId": "ab2b68d2",
"IntegrationMethod": "POST",
"IntegrationType": "AWS_PROXY",
"IntegrationUri": "arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler",
"PayloadFormatVersion": "1.0",
"TimeoutInMillis": 30000
}
]
}
[stdout]
{
"Items": [
{
"ConnectionType": "INTERNET",
"IntegrationId": "ab2b68d2",
"IntegrationMethod": "POST",
"IntegrationType": "AWS_PROXY",
"IntegrationUri": "arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler",
"PayloadFormatVersion": "1.0",
"TimeoutInMillis": 30000
}
]
}{
"command": "aws apigatewayv2 get-routes --api-id 9c815daf --output json",
"description": "Inspect routes"
}{
"Items": [
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"read:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "d143ea4c",
"RouteKey": "GET /things/{id}",
"Target": "integrations/ab2b68d2"
},
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"write:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "50cad8bb",
"RouteKey": "POST /things",
"Target": "integrations/ab2b68d2"
},
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"admin:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "4f949f7d",
"RouteKey": "DELETE /things/{id}",
"Target": "integrations/ab2b68d2"
},
{
"ApiKeyRequired": false,
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "48d697f2",
"RouteKey": "OPTIONS /{proxy+}",
"Target": "integrations/ab2b68d2"
}
]
}
[stdout]
{
"Items": [
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"read:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "d143ea4c",
"RouteKey": "GET /things/{id}",
"Target": "integrations/ab2b68d2"
},
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"write:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "50cad8bb",
"RouteKey": "POST /things",
"Target": "integrations/ab2b68d2"
},
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"admin:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "4f949f7d",
"RouteKey": "DELETE /things/{id}",
"Target": "integrations/ab2b68d2"
},
{
"ApiKeyRequired": false,
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "48d697f2",
"RouteKey": "OPTIONS /{proxy+}",
"Target": "integrations/ab2b68d2"
}
]
}{
"command": "aws apigatewayv2 get-stages --api-id 9c815daf --output json",
"description": "Inspect stages"
}{
"Items": [
{
"AccessLogSettings": {
"DestinationArn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api",
"Format": "{\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"status\":\"$context.status\",\"responseLength\":\"$context.responseLength\"}"
},
"AutoDeploy": false,
"CreatedDate": "2026-05-16T16:17:21.258227+00:00",
"DefaultRouteSettings": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 10,
"ThrottlingRateLimit": 5.0
},
"LastUpdatedDate": "2026-05-16T16:17:21.258227+00:00",
"RouteSettings": {
"GET /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 2000,
"ThrottlingRateLimit": 1000.0
},
"POST /things": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 200,
"ThrottlingRateLimit": 100.0
},
"DELETE /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 20,
"ThrottlingRateLimit": 10.0
}
},
"StageName": "$default",
"StageVariables": {},
"Tags": {}
}
]
}
[stdout]
{
"Items": [
{
"AccessLogSettings": {
"DestinationArn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api",
"Format": "{\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"status\":\"$context.status\",\"responseLength\":\"$context.responseLength\"}"
},
"AutoDeploy": false,
"CreatedDate": "2026-05-16T16:17:21.258227+00:00",
"DefaultRouteSettings": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 10,
"ThrottlingRateLimit": 5.0
},
"LastUpdatedDate": "2026-05-16T16:17:21.258227+00:00",
"RouteSettings": {
"GET /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 2000,
"ThrottlingRateLimit": 1000.0
},
"POST /things": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 200,
"ThrottlingRateLimit": 100.0
},
"DELETE /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 20,
"ThrottlingRateLimit": 10.0
}
},
"StageName": "$default",
"StageVariables": {},
"Tags": {}
}
]
}{
"command": "aws lambda get-function --function-name harbor-api-handler --output json",
"description": "Inspect Lambda function"
}{
"Configuration": {
"FunctionName": "harbor-api-handler",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler",
"Runtime": "python3.11",
"Role": "arn:aws:iam::000000000000:role/harbor-api-handler-role",
"Handler": "handler.handler",
"CodeSize": 383,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2026-05-16T16:17:28.069382+0000",
"CodeSha256": "Z/1vSQlEAng+O2AysqyeHhfjp2zertJC9EXVuL6gjIk=",
"Version": "$LATEST",
"DeadLetterConfig": {
"TargetArn": "arn:aws:sqs:us-east-1:000000000000:harbor-api-dlq"
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "eab13a7e-e693-4228-b6bd-7934fdf43715",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/harbor-api-handler"
}
},
"Code": {
"RepositoryType": "S3",
"Location": "http://localhost.localstack.cloud:4566/awslambda-us-east-1-tasks/snapshots/000000000000/harbor-api-handler-36dac589-4cd3-4636-b969-07c31f41f21f?AWSAccessKeyId=949334387222&Signature=b%2FBKUjY%2BF%2BHwsumHhv6AnqDQ2kY%3D&Expires=1778952001"
}
}
[stdout]
{
"Configuration": {
"FunctionName": "harbor-api-handler",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler",
"Runtime": "python3.11",
"Role": "arn:aws:iam::000000000000:role/harbor-api-handler-role",
"Handler": "handler.handler",
"CodeSize": 383,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2026-05-16T16:17:28.069382+0000",
"CodeSha256": "Z/1vSQlEAng+O2AysqyeHhfjp2zertJC9EXVuL6gjIk=",
"Version": "$LATEST",
"DeadLetterConfig": {
"TargetArn": "arn:aws:sqs:us-east-1:000000000000:harbor-api-dlq"
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "eab13a7e-e693-4228-b6bd-7934fdf43715",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/harbor-api-handler"
}
},
"Code": {
"RepositoryType": "S3",
"Location": "http://localhost.localstack.cloud:4566/awslambda-us-east-1-tasks/snapshots/000000000000/harbor-api-handler-36dac589-4cd3-4636-b969-07c31f41f21f?AWSAccessKeyId=949334387222&Signature=b%2FBKUjY%2BF%2BHwsumHhv6AnqDQ2kY%3D&Expires=1778952001"
}
}{
"command": "aws lambda get-policy --function-name harbor-api-handler --output json",
"description": "Inspect Lambda resource policy"
}{
"Policy": "{\"Version\": \"2012-10-17\", \"Id\": \"default\", \"Statement\": [{\"Sid\": \"apigw-all\", \"Effect\": \"Allow\", \"Action\": \"lambda:InvokeFunction\", \"Resource\": \"arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler\", \"Principal\": {\"Service\": \"apigateway.amazonaws.com\"}, \"Condition\": {\"ArnLike\": {\"AWS:SourceArn\": \"arn:aws:execute-api:us-east-1:000000000000:*/*/*\"}}}]}",
"RevisionId": "eab13a7e-e693-4228-b6bd-7934fdf43715"
}
[stdout]
{
"Policy": "{\"Version\": \"2012-10-17\", \"Id\": \"default\", \"Statement\": [{\"Sid\": \"apigw-all\", \"Effect\": \"Allow\", \"Action\": \"lambda:InvokeFunction\", \"Resource\": \"arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler\", \"Principal\": {\"Service\": \"apigateway.amazonaws.com\"}, \"Condition\": {\"ArnLike\": {\"AWS:SourceArn\": \"arn:aws:execute-api:us-east-1:000000000000:*/*/*\"}}}]}",
"RevisionId": "eab13a7e-e693-4228-b6bd-7934fdf43715"
}{
"command": "aws lambda get-function-url-config --function-name harbor-api-handler --output json 2>&1 || echo \"no function url\"",
"description": "Check for Lambda function URL"
}{
"FunctionUrl": "http://hgfy1wmin3zjsfxm6rt3ysn9jlgcidag.lambda-url.us-east-1.localhost.localstack.cloud:4566/",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler",
"AuthType": "NONE",
"CreationTime": "2026-05-16T16:17:23.368844+0000",
"LastModifiedTime": "2026-05-16T16:17:23.368886+0000"
}
[stdout]
{
"FunctionUrl": "http://hgfy1wmin3zjsfxm6rt3ysn9jlgcidag.lambda-url.us-east-1.localhost.localstack.cloud:4566/",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler",
"AuthType": "NONE",
"CreationTime": "2026-05-16T16:17:23.368844+0000",
"LastModifiedTime": "2026-05-16T16:17:23.368886+0000"
}{
"command": "aws lambda get-function-event-invoke-config --function-name harbor-api-handler --output json 2>&1 || echo \"no event invoke config\"",
"description": "Check Lambda async invocation config"
}aws: [ERROR]: An error occurred (ResourceNotFoundException) when calling the GetFunctionEventInvokeConfig operation: The function arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler:$LATEST doesn't have an EventInvokeConfig Additional error details: Type: User no event invoke config [stdout] aws: [ERROR]: An error occurred (ResourceNotFoundException) when calling the GetFunctionEventInvokeConfig operation: The function arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler:$LATEST doesn't have an EventInvokeConfig Additional error details: Type: User no event invoke config
{
"command": "aws cognito-idp describe-user-pool --user-pool-id us-east-1_7981fafba8e94b82a5960d0b4f864c84 --output json",
"description": "Inspect Cognito user pool"
}{
"UserPool": {
"Id": "us-east-1_7981fafba8e94b82a5960d0b4f864c84",
"Name": "harbor-api-pool",
"Policies": {
"PasswordPolicy": {
"MinimumLength": 8,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireNumbers": true,
"RequireSymbols": true,
"TemporaryPasswordValidityDays": 7
}
},
"DeletionProtection": "INACTIVE",
"LambdaConfig": {},
"LastModifiedDate": "2026-05-16T16:17:05.098297+00:00",
"CreationDate": "2026-05-16T16:17:05.098300+00:00",
"SchemaAttributes": [
{
"Name": "sub",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": false,
"Required": true,
"StringAttributeConstraints": {
"MinLength": "1",
"MaxLength": "2048"
}
},
{
"Name": "name",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "given_name",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "family_name",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "middle_name",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "nickname",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "preferred_username",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "profile",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "picture",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
…[truncated]{
"command": "aws cognito-idp describe-user-pool-client --user-pool-id us-east-1_7981fafba8e94b82a5960d0b4f864c84 --client-id pu0pmgrnf9omi6zjwvwpvcyjwh --output json",
"description": "Inspect Cognito app client"
}{
"UserPoolClient": {
"UserPoolId": "us-east-1_7981fafba8e94b82a5960d0b4f864c84",
"ClientName": "harbor-api-client",
"ClientId": "pu0pmgrnf9omi6zjwvwpvcyjwh",
"ClientSecret": "8dd8e926",
"LastModifiedDate": "2026-05-16T16:17:09.040773+00:00",
"CreationDate": "2026-05-16T16:17:09.040776+00:00",
"RefreshTokenValidity": 30,
"TokenValidityUnits": {},
"ExplicitAuthFlows": [
"ALLOW_REFRESH_TOKEN_AUTH"
],
"SupportedIdentityProviders": [
"COGNITO"
],
"AllowedOAuthFlowsUserPoolClient": false,
"EnableTokenRevocation": true,
"EnablePropagateAdditionalUserContextData": false,
"AuthSessionValidity": 3
}
}
[stdout]
{
"UserPoolClient": {
"UserPoolId": "us-east-1_7981fafba8e94b82a5960d0b4f864c84",
"ClientName": "harbor-api-client",
"ClientId": "pu0pmgrnf9omi6zjwvwpvcyjwh",
"ClientSecret": "8dd8e926",
"LastModifiedDate": "2026-05-16T16:17:09.040773+00:00",
"CreationDate": "2026-05-16T16:17:09.040776+00:00",
"RefreshTokenValidity": 30,
"TokenValidityUnits": {},
"ExplicitAuthFlows": [
"ALLOW_REFRESH_TOKEN_AUTH"
],
"SupportedIdentityProviders": [
"COGNITO"
],
"AllowedOAuthFlowsUserPoolClient": false,
"EnableTokenRevocation": true,
"EnablePropagateAdditionalUserContextData": false,
"AuthSessionValidity": 3
}
}{
"command": "aws cognito-idp describe-resource-server --user-pool-id us-east-1_7981fafba8e94b82a5960d0b4f864c84 --identifier harbor-api --output json",
"description": "Inspect Cognito resource server"
}{
"ResourceServer": {
"UserPoolId": "us-east-1_7981fafba8e94b82a5960d0b4f864c84",
"Identifier": "harbor-api",
"Name": "harbor-api-rs",
"Scopes": [
{
"ScopeName": "read:things",
"ScopeDescription": "Read things"
},
{
"ScopeName": "write:things",
"ScopeDescription": "Write things"
},
{
"ScopeName": "admin:things",
"ScopeDescription": "Admin things"
}
]
}
}
[stdout]
{
"ResourceServer": {
"UserPoolId": "us-east-1_7981fafba8e94b82a5960d0b4f864c84",
"Identifier": "harbor-api",
"Name": "harbor-api-rs",
"Scopes": [
{
"ScopeName": "read:things",
"ScopeDescription": "Read things"
},
{
"ScopeName": "write:things",
"ScopeDescription": "Write things"
},
{
"ScopeName": "admin:things",
"ScopeDescription": "Admin things"
}
]
}
}{
"query": "select:TaskCreate,TaskUpdate,TaskList",
"max_results": 5
}{"type": "tool_reference", "tool_name": "TaskCreate"}
{"type": "tool_reference", "tool_name": "TaskUpdate"}
{"type": "tool_reference", "tool_name": "TaskList"}
{
"command": "aws lambda get-function --function-name harbor-api-handler --query 'Code.Location' --output text | xargs -I {} curl -sL \"{}\" -o /tmp/lambda-code.zip && ls -la /tmp/lambda-code.zip && cd /tmp && unzip -o lambda-code.zip -d lambda-code/ && ls lambda-code/",
"description": "Download and inspect Lambda code"
}Exit code 123 [error] tool reported failure
{
"command": "aws iam get-role --role-name harbor-api-handler-role --output json",
"description": "Inspect IAM role"
}{
"Role": {
"Path": "/",
"RoleName": "harbor-api-handler-role",
"RoleId": "AROAQAAAAAAANMSQOSP4B",
"Arn": "arn:aws:iam::000000000000:role/harbor-api-handler-role",
"CreateDate": "2026-05-16T16:17:09.714527+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"MaxSessionDuration": 3600,
"RoleLastUsed": {}
}
}
[stdout]
{
"Role": {
"Path": "/",
"RoleName": "harbor-api-handler-role",
"RoleId": "AROAQAAAAAAANMSQOSP4B",
"Arn": "arn:aws:iam::000000000000:role/harbor-api-handler-role",
"CreateDate": "2026-05-16T16:17:09.714527+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"MaxSessionDuration": 3600,
"RoleLastUsed": {}
}
}{
"command": "aws iam list-attached-role-policies --role-name harbor-api-handler-role --output json; aws iam list-role-policies --role-name harbor-api-handler-role --output json",
"description": "List attached/inline policies for role"
}{
"AttachedPolicies": [
{
"PolicyName": "AWSLambdaBasicExecutionRole",
"PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
]
}
{
"PolicyNames": []
}
[stdout]
{
"AttachedPolicies": [
{
"PolicyName": "AWSLambdaBasicExecutionRole",
"PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
]
}
{
"PolicyNames": []
}{
"command": "aws kms describe-key --key-id b9ab89c5-7a36-4982-b4be-5452bd533b55 --output json; aws kms get-key-policy --key-id b9ab89c5-7a36-4982-b4be-5452bd533b55 --policy-name default --output json; aws kms get-key-rotation-status --key-id b9ab89c5-7a36-4982-b4be-5452bd533b55 --output json",
"description": "Inspect KMS key"
}{
"KeyMetadata": {
"AWSAccountId": "000000000000",
"KeyId": "b9ab89c5-7a36-4982-b4be-5452bd533b55",
"Arn": "arn:aws:kms:us-east-1:000000000000:key/b9ab89c5-7a36-4982-b4be-5452bd533b55",
"CreationDate": "2026-05-16T16:17:24.386868+00:00",
"Enabled": true,
"Description": "",
"KeyUsage": "ENCRYPT_DECRYPT",
"KeyState": "Enabled",
"Origin": "AWS_KMS",
"KeyManager": "CUSTOMER",
"CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
"KeySpec": "SYMMETRIC_DEFAULT",
"EncryptionAlgorithms": [
"SYMMETRIC_DEFAULT"
],
"MultiRegion": false,
"CurrentKeyMaterialId": "def6db4c861955eeb673716778b3315fdef6db4c861955eeb673716778b3315f"
}
}
{
"Policy": "{\n \"Version\":\"2012-10-17\",\n \"Statement\":[\n {\n \"Sid\":\"RootAdmin\",\n \"Effect\":\"Allow\",\n \"Principal\":{\"AWS\":\"arn:aws:iam::000000000000:root\"},\n \"Action\":\"kms:*\",\n \"Resource\":\"*\"\n }\n ]\n}\n"
}
{
"KeyRotationEnabled": false,
"KeyId": "arn:aws:kms:us-east-1:000000000000:key/b9ab89c5-7a36-4982-b4be-5452bd533b55"
}
[stdout]
{
"KeyMetadata": {
"AWSAccountId": "000000000000",
"KeyId": "b9ab89c5-7a36-4982-b4be-5452bd533b55",
"Arn": "arn:aws:kms:us-east-1:000000000000:key/b9ab89c5-7a36-4982-b4be-5452bd533b55",
"CreationDate": "2026-05-16T16:17:24.386868+00:00",
"Enabled": true,
"Description": "",
"KeyUsage": "ENCRYPT_DECRYPT",
"KeyState": "Enabled",
"Origin": "AWS_KMS",
"KeyManager": "CUSTOMER",
"CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
"KeySpec": "SYMMETRIC_DEFAULT",
"EncryptionAlgorithms": [
"SYMMETRIC_DEFAULT"
],
"MultiRegion": false,
"CurrentKeyMaterialId": "def6db4c861955eeb673716778b3315fdef6db4c861955eeb673716778b3315f"
}
}
{
"Policy": "{\n \"Version\":\"2012-10-17\",\n \"Statement\":[\n {\n \"Sid\":\"RootAdmin\",\n \"Effect\":\"Allow\",\n \"Principal\":{\"AWS\":\"arn:aws:iam::000000000000:root\"},\n \"Action\":\"kms:*\",\n \"Resource\":\"*\"\n }\n ]\n}\n"
}
{
"KeyRotationEnabled": false,
"KeyId": "arn:aws:kms:us-east-1:000000000000:key/b9ab89c5-7a36-4982-b4be-5452bd533b55"
}{
"command": "aws logs describe-log-groups --log-group-name-prefix /aws/apigw/harbor-api --output json",
"description": "Inspect access log group"
}{
"logGroups": [
{
"logGroupName": "/aws/apigw/harbor-api",
"creationTime": 1778948235854,
"metricFilterCount": 0,
"arn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api:*",
"storedBytes": 0,
"logGroupClass": "STANDARD",
"logGroupArn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api"
}
]
}
[stdout]
{
"logGroups": [
{
"logGroupName": "/aws/apigw/harbor-api",
"creationTime": 1778948235854,
"metricFilterCount": 0,
"arn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api:*",
"storedBytes": 0,
"logGroupClass": "STANDARD",
"logGroupArn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api"
}
]
}{
"command": "aws apigatewayv2 get-domain-name --domain-name api.harbor.example --output json",
"description": "Inspect custom domain"
}{
"ApiMappingSelectionExpression": "$request.basepath",
"DomainName": "api.harbor.example",
"DomainNameArn": "arn:aws:apigateway:us-east-1::/domainnames/api.harbor.example",
"DomainNameConfigurations": [
{
"ApiGatewayDomainName": "d-6923abfa.execute-api.localhost.localstack.cloud",
"CertificateArn": "arn:aws:acm:us-east-1:000000000000:certificate/d1256c72-6a6c-4645-a1b8-77c3c25d5bca",
"DomainNameStatus": "AVAILABLE",
"EndpointType": "REGIONAL",
"HostedZoneId": "655c2816",
"IpAddressType": "ipv4",
"SecurityPolicy": "TLS_1_0"
}
],
"RoutingMode": "API_MAPPING_ONLY",
"Tags": {}
}
[stdout]
{
"ApiMappingSelectionExpression": "$request.basepath",
"DomainName": "api.harbor.example",
"DomainNameArn": "arn:aws:apigateway:us-east-1::/domainnames/api.harbor.example",
"DomainNameConfigurations": [
{
"ApiGatewayDomainName": "d-6923abfa.execute-api.localhost.localstack.cloud",
"CertificateArn": "arn:aws:acm:us-east-1:000000000000:certificate/d1256c72-6a6c-4645-a1b8-77c3c25d5bca",
"DomainNameStatus": "AVAILABLE",
"EndpointType": "REGIONAL",
"HostedZoneId": "655c2816",
"IpAddressType": "ipv4",
"SecurityPolicy": "TLS_1_0"
}
],
"RoutingMode": "API_MAPPING_ONLY",
"Tags": {}
}{
"command": "aws cloudwatch describe-alarms --alarm-names harbor-api-5xx-alarm --output json",
"description": "Inspect CloudWatch alarm"
}{
"MetricAlarms": [
{
"AlarmName": "harbor-api-5xx-alarm",
"AlarmArn": "arn:aws:cloudwatch:us-east-1:000000000000:alarm:harbor-api-5xx-alarm",
"AlarmConfigurationUpdatedTimestamp": "2026-05-16T16:17:29.492863+00:00",
"ActionsEnabled": true,
"OKActions": [],
"AlarmActions": [],
"InsufficientDataActions": [],
"StateValue": "OK",
"StateReason": "Threshold Crossed: no datapoints were received for 1 period and 1 missing datapoint was treated as [NonBreaching].",
"StateReasonData": "{\"version\": \"1.0\", \"queryDate\": \"2026-05-16T16:17:29+0000\", \"period\": 60, \"recentDatapoints\": [], \"threshold\": 0.0, \"statistic\": \"Sum\", \"startDate\": \"2026-05-16T16:17:29+0000\"}",
"StateUpdatedTimestamp": "2026-05-16T16:17:29.548257+00:00",
"MetricName": "5xx",
"Namespace": "AWS/ApiGateway",
"Statistic": "Sum",
"Dimensions": [
{
"Name": "ApiName",
"Value": "harbor-api"
},
{
"Name": "Stage",
"Value": "$default"
}
],
"Period": 60,
"EvaluationPeriods": 1,
"Threshold": 0.0,
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"TreatMissingData": "notBreaching",
"StateTransitionedTimestamp": "2026-05-16T16:17:29.548252+00:00"
}
],
"CompositeAlarms": []
}
[stdout]
{
"MetricAlarms": [
{
"AlarmName": "harbor-api-5xx-alarm",
"AlarmArn": "arn:aws:cloudwatch:us-east-1:000000000000:alarm:harbor-api-5xx-alarm",
"AlarmConfigurationUpdatedTimestamp": "2026-05-16T16:17:29.492863+00:00",
"ActionsEnabled": true,
"OKActions": [],
"AlarmActions": [],
"InsufficientDataActions": [],
"StateValue": "OK",
"StateReason": "Threshold Crossed: no datapoints were received for 1 period and 1 missing datapoint was treated as [NonBreaching].",
"StateReasonData": "{\"version\": \"1.0\", \"queryDate\": \"2026-05-16T16:17:29+0000\", \"period\": 60, \"recentDatapoints\": [], \"threshold\": 0.0, \"statistic\": \"Sum\", \"startDate\": \"2026-05-16T16:17:29+0000\"}",
"StateUpdatedTimestamp": "2026-05-16T16:17:29.548257+00:00",
"MetricName": "5xx",
"Namespace": "AWS/ApiGateway",
"Statistic": "Sum",
"Dimensions": [
{
"Name": "ApiName",
"Value": "harbor-api"
},
{
"Name": "Stage",
"Value": "$default"
}
],
"Period": 60,
"EvaluationPeriods": 1,
"Threshold": 0.0,
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"TreatMissingData": "notBreaching",
"StateTransitionedTimestamp": "2026-05-16T16:17:29.548252+00:00"
}
],
"CompositeAlarms": []
}{
"command": "aws sqs get-queue-attributes --queue-url $(aws sqs get-queue-url --queue-name harbor-api-dlq --query QueueUrl --output text) --attribute-names All --output json",
"description": "Inspect DLQ"
}{
"Attributes": {
"CreatedTimestamp": "1778948247",
"DelaySeconds": "0",
"LastModifiedTimestamp": "1778948247",
"MaximumMessageSize": "1048576",
"MessageRetentionPeriod": "345600",
"QueueArn": "arn:aws:sqs:us-east-1:000000000000:harbor-api-dlq",
"ReceiveMessageWaitTimeSeconds": "0",
"VisibilityTimeout": "30",
"SqsManagedSseEnabled": "true",
"ApproximateNumberOfMessages": "0",
"ApproximateNumberOfMessagesDelayed": "0",
"ApproximateNumberOfMessagesNotVisible": "0"
}
}
[stdout]
{
"Attributes": {
"CreatedTimestamp": "1778948247",
"DelaySeconds": "0",
"LastModifiedTimestamp": "1778948247",
"MaximumMessageSize": "1048576",
"MessageRetentionPeriod": "345600",
"QueueArn": "arn:aws:sqs:us-east-1:000000000000:harbor-api-dlq",
"ReceiveMessageWaitTimeSeconds": "0",
"VisibilityTimeout": "30",
"SqsManagedSseEnabled": "true",
"ApproximateNumberOfMessages": "0",
"ApproximateNumberOfMessagesDelayed": "0",
"ApproximateNumberOfMessagesNotVisible": "0"
}
}{
"command": "URL=$(aws lambda get-function --function-name harbor-api-handler --query 'Code.Location' --output text); echo \"URL=$URL\"; curl -sL \"$URL\" -o /tmp/lambda-code.zip; ls -la /tmp/lambda-code.zip; file /tmp/lambda-code.zip; cd /tmp && unzip -o lambda-code.zip -d lambda-code/ 2>&1 | head -50; ls /tmp/lambda-code/",
"description": "Download lambda code with proper error handling"
}Exit code 2 URL=http://localhost.localstack.cloud:4566/awslambda-us-east-1-tasks/snapshots/000000000000/harbor-api-handler-36dac589-4cd3-4636-b969-07c31f41f21f?AWSAccessKeyId=949334387222&Signature=QNkmME7BNSoe8Sj8aiUVf8Zh890%3D&Expires=1778952036 ls: cannot access '/tmp/lambda-code.zip': No such file or directory /bin/bash: line 1: file: command not found unzip: cannot find or open lambda-code.zip, lambda-code.zip.zip or lambda-code.zip.ZIP. ls: cannot access '/tmp/lambda-code/': No such file or directory [error] tool reported failure
{
"command": "aws logs describe-log-groups --log-group-name-prefix /aws/apigw/harbor-api --output json --query 'logGroups[0].[retentionInDays,kmsKeyId]'",
"description": "Check log group retention and KMS"
}[
null,
null
]
[stdout]
[
null,
null
]{
"command": "aws sns get-topic-attributes --topic-arn arn:aws:sns:us-east-1:000000000000:harbor-api-alarms --output json",
"description": "Inspect SNS topic"
}{
"Attributes": {
"DisplayName": "",
"Owner": "000000000000",
"Policy": "{\"Version\": \"2008-10-17\", \"Id\": \"__default_policy_ID\", \"Statement\": [{\"Effect\": \"Allow\", \"Sid\": \"__default_statement_ID\", \"Principal\": {\"AWS\": \"*\"}, \"Action\": [\"SNS:GetTopicAttributes\", \"SNS:SetTopicAttributes\", \"SNS:AddPermission\", \"SNS:RemovePermission\", \"SNS:DeleteTopic\", \"SNS:Subscribe\", \"SNS:ListSubscriptionsByTopic\", \"SNS:Publish\"], \"Resource\": \"arn:aws:sns:us-east-1:000000000000:harbor-api-alarms\", \"Condition\": {\"StringEquals\": {\"AWS:SourceOwner\": \"000000000000\"}}}]}",
"SubscriptionsConfirmed": "0",
"SubscriptionsDeleted": "0",
"SubscriptionsPending": "0",
"TopicArn": "arn:aws:sns:us-east-1:000000000000:harbor-api-alarms",
"EffectiveDeliveryPolicy": "{\"http\": {\"defaultHealthyRetryPolicy\": {\"minDelayTarget\": 20, \"maxDelayTarget\": 20, \"numRetries\": 3, \"numMaxDelayRetries\": 0, \"numNoDelayRetries\": 0, \"numMinDelayRetries\": 0, \"backoffFunction\": \"linear\"}, \"disableSubscriptionOverrides\": false, \"defaultRequestPolicy\": {\"headerContentType\": \"text/plain; charset=UTF-8\"}}}"
}
}
[stdout]
{
"Attributes": {
"DisplayName": "",
"Owner": "000000000000",
"Policy": "{\"Version\": \"2008-10-17\", \"Id\": \"__default_policy_ID\", \"Statement\": [{\"Effect\": \"Allow\", \"Sid\": \"__default_statement_ID\", \"Principal\": {\"AWS\": \"*\"}, \"Action\": [\"SNS:GetTopicAttributes\", \"SNS:SetTopicAttributes\", \"SNS:AddPermission\", \"SNS:RemovePermission\", \"SNS:DeleteTopic\", \"SNS:Subscribe\", \"SNS:ListSubscriptionsByTopic\", \"SNS:Publish\"], \"Resource\": \"arn:aws:sns:us-east-1:000000000000:harbor-api-alarms\", \"Condition\": {\"StringEquals\": {\"AWS:SourceOwner\": \"000000000000\"}}}]}",
"SubscriptionsConfirmed": "0",
"SubscriptionsDeleted": "0",
"SubscriptionsPending": "0",
"TopicArn": "arn:aws:sns:us-east-1:000000000000:harbor-api-alarms",
"EffectiveDeliveryPolicy": "{\"http\": {\"defaultHealthyRetryPolicy\": {\"minDelayTarget\": 20, \"maxDelayTarget\": 20, \"numRetries\": 3, \"numMaxDelayRetries\": 0, \"numNoDelayRetries\": 0, \"numMinDelayRetries\": 0, \"backoffFunction\": \"linear\"}, \"disableSubscriptionOverrides\": false, \"defaultRequestPolicy\": {\"headerContentType\": \"text/plain; charset=UTF-8\"}}}"
}
}{
"command": "aws apigatewayv2 get-api-mappings --domain-name api.harbor.example --output json",
"description": "Inspect API mappings on custom domain"
}{
"Items": [
{
"ApiId": "9c815daf",
"ApiMappingId": "b321046f",
"ApiMappingKey": "",
"Stage": "$default"
}
]
}
[stdout]
{
"Items": [
{
"ApiId": "9c815daf",
"ApiMappingId": "b321046f",
"ApiMappingKey": "",
"Stage": "$default"
}
]
}{
"command": "URL=$(aws lambda get-function --function-name harbor-api-handler --query 'Code.Location' --output text); curl -sL -o /tmp/lambda-code.zip \"$URL\"; ls -la /tmp/lambda-code.zip 2>&1",
"description": "Re-download Lambda code"
}Exit code 2 ls: cannot access '/tmp/lambda-code.zip': No such file or directory [error] tool reported failure
{
"command": "URL=$(aws lambda get-function --function-name harbor-api-handler --query 'Code.Location' --output text); echo \"URL: $URL\"; curl -v -L -o /tmp/lambda-code.zip \"$URL\" 2>&1 | tail -30",
"description": "Verbose curl to see why download fails"
}URL: http://localhost.localstack.cloud:4566/awslambda-us-east-1-tasks/snapshots/000000000000/harbor-api-handler-36dac589-4cd3-4636-b969-07c31f41f21f?AWSAccessKeyId=949334387222&Signature=86JitwEPDbN9vum1VFai2dVRuQI%3D&Expires=1778952046
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Host localhost.localstack.cloud:4566 was resolved.
* IPv6: (none)
* IPv4: 127.0.0.1
* Trying 127.0.0.1:4566...
* connect to 127.0.0.1 port 4566 from 127.0.0.1 port 38040 failed: Connection refused
* Failed to connect to localhost.localstack.cloud port 4566 after 14 ms: Couldn't connect to server
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
* Closing connection
curl: (7) Failed to connect to localhost.localstack.cloud port 4566 after 14 ms: Couldn't connect to server
[stdout]
URL: http://localhost.localstack.cloud:4566/awslambda-us-east-1-tasks/snapshots/000000000000/harbor-api-handler-36dac589-4cd3-4636-b969-07c31f41f21f?AWSAccessKeyId=949334387222&Signature=86JitwEPDbN9vum1VFai2dVRuQI%3D&Expires=1778952046
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Host localhost.localstack.cloud:4566 was resolved.
* IPv6: (none)
* IPv4: 127.0.0.1
* Trying 127.0.0.1:4566...
* connect to 127.0.0.1 port 4566 from 127.0.0.1 port 38040 failed: Connection refused
* Failed to connect to localhost.localstack.cloud port 4566 after 14 ms: Couldn't connect to server
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
* Closing connection
curl: (7) Failed to connect to localhost.localstack.cloud port 4566 after 14 ms: Couldn't connect to server{
"command": "URL=$(aws lambda get-function --function-name harbor-api-handler --query 'Code.Location' --output text); URL_FIXED=\"${URL/localhost.localstack.cloud:4566/localstack:4566}\"; echo \"URL: $URL_FIXED\"; curl -sL -o /tmp/lambda-code.zip \"$URL_FIXED\"; ls -la /tmp/lambda-code.zip 2>&1; cd /tmp && unzip -o lambda-code.zip -d lambda-code/ && ls -la /tmp/lambda-code/",
"description": "Download Lambda code using localstack hostname"
}URL: http://localstack:4566/awslambda-us-east-1-tasks/snapshots/000000000000/harbor-api-handler-36dac589-4cd3-4636-b969-07c31f41f21f?AWSAccessKeyId=949334387222&Signature=yoHSNnDE1W1KWG6GcOTX1VPjsKk%3D&Expires=1778952052 -rw-r--r-- 1 root root 383 May 16 16:20 /tmp/lambda-code.zip Archive: lambda-code.zip inflating: lambda-code/handler.py total 12 drwxr-xr-x 2 root root 4096 May 16 16:20 . drwxrwxrwt 1 root root 4096 May 16 16:20 .. -rw-r--r-- 1 root root 345 May 16 16:17 handler.py Shell cwd was reset to /app [stdout] URL: http://localstack:4566/awslambda-us-east-1-tasks/snapshots/000000000000/harbor-api-handler-36dac589-4cd3-4636-b969-07c31f41f21f?AWSAccessKeyId=949334387222&Signature=yoHSNnDE1W1KWG6GcOTX1VPjsKk%3D&Expires=1778952052 -rw-r--r-- 1 root root 383 May 16 16:20 /tmp/lambda-code.zip Archive: lambda-code.zip inflating: lambda-code/handler.py total 12 drwxr-xr-x 2 root root 4096 May 16 16:20 . drwxrwxrwt 1 root root 4096 May 16 16:20 .. -rw-r--r-- 1 root root 345 May 16 16:17 handler.py [stderr] Shell cwd was reset to /app
{
"file_path": "/tmp/lambda-code/handler.py"
}1 import json
2 def handler(event, context):
3 rk = (event.get("requestContext") or {}).get("http", {}).get("method", "?")
4 path = event.get("rawPath", "?")
5 pp = event.get("pathParameters") or {}
6 return {"statusCode": 200, "headers": {"Content-Type": "application/json"}, "body": json.dumps({"method": rk, "path": path, "params": pp})}
7
{
"command": "aws cognito-idp list-identity-providers --user-pool-id us-east-1_7981fafba8e94b82a5960d0b4f864c84 --output json",
"description": "List user pool identity providers"
}{
"Providers": []
}
[stdout]
{
"Providers": []
}{
"command": "aws cognito-idp describe-user-pool-domain --domain harbor-api-domain --output json 2>&1",
"description": "Check user pool domain"
}Exit code 254 aws: [ERROR]: An error occurred (ResourceNotFoundException) when calling the DescribeUserPoolDomain operation: Unable to find user pool domain harbor-api-domain [error] tool reported failure
{
"command": "aws cognito-idp describe-user-pool --user-pool-id us-east-1_7981fafba8e94b82a5960d0b4f864c84 --query 'UserPool.Domain' --output json; aws cognito-idp describe-user-pool --user-pool-id us-east-1_7981fafba8e94b82a5960d0b4f864c84 --query 'UserPool.CustomDomain' --output json",
"description": "Check user pool domain field"
}null null [stdout] null null
{
"subject": "Fix route scope strings to include resource server prefix",
"description": "Update GET/POST/DELETE scopes to harbor-api/read:things etc"
}Task #1 created successfully: Fix route scope strings to include resource server prefix
{
"subject": "Set log group retention",
"description": "Set retentionInDays on /aws/apigw/harbor-api"
}Task #2 created successfully: Set log group retention
{
"subject": "Enable KMS key rotation",
"description": "Enable automatic key rotation on the access-log CMK"
}Task #3 created successfully: Enable KMS key rotation
{
"subject": "Encrypt log group with CMK",
"description": "Associate the KMS key with the access log group (it currently has no KMS key)"
}Task #4 created successfully: Encrypt log group with CMK
{
"subject": "Fix JWT authorizer issuer + audience",
"description": "Update authorizer 5a5a06e0 to issue from our pool (us-east-1_7981fafba8e94b82a5960d0b4f864c84) and audience to app client id (pu0pmgrnf9omi6zjwvwpvcyjwh)"
}Task #5 created successfully: Fix JWT authorizer issuer + audience
{
"subject": "Tighten CORS config",
"description": "Replace wildcard AllowMethods with specific list, set MaxAge to a real value"
}Task #6 created successfully: Tighten CORS config
{
"subject": "Wire CloudWatch alarm to SNS",
"description": "Set AlarmActions to the SNS topic ARN"
}Task #7 created successfully: Wire CloudWatch alarm to SNS
{
"subject": "Recreate integration with PayloadFormatVersion 2.0",
"description": "Lambda code expects v2 event shape; current is 1.0. Replace integration and repoint routes"
}Task #8 created successfully: Recreate integration with PayloadFormatVersion 2.0
{
"subject": "Enable client_credentials OAuth flow on app client",
"description": "Set AllowedOAuthFlowsUserPoolClient=true, AllowedOAuthFlows=[client_credentials], AllowedOAuthScopes=[harbor-api/*]"
}Task #9 created successfully: Enable client_credentials OAuth flow on app client
{
"subject": "Remove unauthorized identity provider from app client",
"description": "SupportedIdentityProviders list , strip any provider the API didn't authorize"
}Task #10 created successfully: Remove unauthorized identity provider from app client
{
"subject": "Delete Lambda function URL",
"description": "Remove function URL to eliminate bypass surface"
}Task #11 created successfully: Delete Lambda function URL
{
"subject": "Raise custom domain TLS floor",
"description": "SecurityPolicy TLS_1_0 -> TLS_1_2"
}Task #12 created successfully: Raise custom domain TLS floor
{
"subject": "Enable stage AutoDeploy",
"description": "Set AutoDeploy=true on $default stage"
}Task #13 created successfully: Enable stage AutoDeploy
{
"subject": "Fix KMS key policy to allow CloudWatch Logs",
"description": "Grant logs.us-east-1.amazonaws.com service principal kms:Encrypt/Decrypt/etc"
}Task #14 created successfully: Fix KMS key policy to allow CloudWatch Logs
{
"subject": "Update access log format with DataDog fields",
"description": "Add requestId, sourceIp, userAgent, routeKey, protocol etc"
}Task #15 created successfully: Update access log format with DataDog fields
{
"subject": "Make OPTIONS route unauthenticated",
"description": "Change OPTIONS /{proxy+} AuthorizationType to NONE so CORS preflight works"
}Task #16 created successfully: Make OPTIONS route unauthenticated
{
"subject": "Grant Lambda role DLQ SendMessage permission",
"description": "Add inline policy for sqs:SendMessage on harbor-api-dlq"
}Task #17 created successfully: Grant Lambda role DLQ SendMessage permission
{
"subject": "Scope Lambda resource policy SourceArn",
"description": "Replace */*/* with this API's id pattern"
}Task #18 created successfully: Scope Lambda resource policy SourceArn
{
"taskId": "5",
"status": "in_progress"
}Updated task #5 status
{
"command": "aws apigatewayv2 update-authorizer --api-id 9c815daf --authorizer-id 5a5a06e0 \\\n --jwt-configuration \"Audience=pu0pmgrnf9omi6zjwvwpvcyjwh,Issuer=https://cognito-idp.us-east-1.amazonaws.com/us-east-1_7981fafba8e94b82a5960d0b4f864c84\" \\\n --output json",
"description": "Fix authorizer issuer and audience"
}{
"AuthorizerId": "5a5a06e0",
"AuthorizerType": "JWT",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": [
"pu0pmgrnf9omi6zjwvwpvcyjwh"
],
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_7981fafba8e94b82a5960d0b4f864c84"
},
"Name": "harbor-jwt-auth"
}
[stdout]
{
"AuthorizerId": "5a5a06e0",
"AuthorizerType": "JWT",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": [
"pu0pmgrnf9omi6zjwvwpvcyjwh"
],
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_7981fafba8e94b82a5960d0b4f864c84"
},
"Name": "harbor-jwt-auth"
}{
"taskId": "1",
"status": "in_progress"
}Updated task #1 status
{
"taskId": "5",
"status": "completed"
}Updated task #5 status
{
"command": "aws apigatewayv2 update-route --api-id 9c815daf --route-id d143ea4c --authorization-scopes harbor-api/read:things --output json",
"description": "Update GET route scope"
}{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"harbor-api/read:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "d143ea4c",
"RouteKey": "GET /things/{id}",
"Target": "integrations/ab2b68d2"
}
[stdout]
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"harbor-api/read:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "d143ea4c",
"RouteKey": "GET /things/{id}",
"Target": "integrations/ab2b68d2"
}{
"command": "aws apigatewayv2 update-route --api-id 9c815daf --route-id 50cad8bb --authorization-scopes harbor-api/write:things --output json",
"description": "Update POST route scope"
}{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"harbor-api/write:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "50cad8bb",
"RouteKey": "POST /things",
"Target": "integrations/ab2b68d2"
}
[stdout]
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"harbor-api/write:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "50cad8bb",
"RouteKey": "POST /things",
"Target": "integrations/ab2b68d2"
}{
"command": "aws apigatewayv2 update-route --api-id 9c815daf --route-id 4f949f7d --authorization-scopes harbor-api/admin:things --output json",
"description": "Update DELETE route scope"
}{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"harbor-api/admin:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "4f949f7d",
"RouteKey": "DELETE /things/{id}",
"Target": "integrations/ab2b68d2"
}
[stdout]
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"harbor-api/admin:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "4f949f7d",
"RouteKey": "DELETE /things/{id}",
"Target": "integrations/ab2b68d2"
}{
"taskId": "16",
"status": "in_progress"
}Updated task #16 status
{
"taskId": "1",
"status": "completed"
}Updated task #1 status
{
"command": "aws apigatewayv2 update-route --api-id 9c815daf --route-id 48d697f2 --authorization-type NONE --output json",
"description": "Set OPTIONS route to NONE auth"
}{
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"RouteId": "48d697f2",
"RouteKey": "OPTIONS /{proxy+}",
"Target": "integrations/ab2b68d2"
}
[stdout]
{
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"RouteId": "48d697f2",
"RouteKey": "OPTIONS /{proxy+}",
"Target": "integrations/ab2b68d2"
}{
"taskId": "8",
"status": "in_progress"
}Updated task #8 status
{
"taskId": "16",
"status": "completed"
}Updated task #16 status
{
"command": "aws apigatewayv2 update-integration --api-id 9c815daf --integration-id ab2b68d2 --payload-format-version 2.0 --output json 2>&1",
"description": "Try updating PayloadFormatVersion in place"
}{
"ConnectionType": "INTERNET",
"IntegrationId": "ab2b68d2",
"IntegrationMethod": "POST",
"IntegrationType": "AWS_PROXY",
"IntegrationUri": "arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler",
"PayloadFormatVersion": "2.0",
"TimeoutInMillis": 30000
}
[stdout]
{
"ConnectionType": "INTERNET",
"IntegrationId": "ab2b68d2",
"IntegrationMethod": "POST",
"IntegrationType": "AWS_PROXY",
"IntegrationUri": "arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler",
"PayloadFormatVersion": "2.0",
"TimeoutInMillis": 30000
}{
"taskId": "13",
"status": "in_progress"
}Updated task #13 status
{
"taskId": "8",
"status": "completed"
}Updated task #8 status
{
"taskId": "6",
"status": "in_progress"
}Updated task #6 status
{
"taskId": "15",
"status": "in_progress"
}Updated task #15 status
{
"command": "aws apigatewayv2 update-stage --api-id 9c815daf --stage-name '$default' --auto-deploy --output json",
"description": "Enable AutoDeploy on stage"
}{
"AccessLogSettings": {
"DestinationArn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api",
"Format": "{\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"status\":\"$context.status\",\"responseLength\":\"$context.responseLength\"}"
},
"AutoDeploy": true,
"CreatedDate": "2026-05-16T16:17:21.258227+00:00",
"DefaultRouteSettings": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 10,
"ThrottlingRateLimit": 5.0
},
"LastUpdatedDate": "2026-05-16T16:17:21.258227+00:00",
"RouteSettings": {
"GET /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 2000,
"ThrottlingRateLimit": 1000.0
},
"POST /things": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 200,
"ThrottlingRateLimit": 100.0
},
"DELETE /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 20,
"ThrottlingRateLimit": 10.0
}
},
"StageName": "$default",
"StageVariables": {},
"Tags": {}
}
[stdout]
{
"AccessLogSettings": {
"DestinationArn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api",
"Format": "{\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"status\":\"$context.status\",\"responseLength\":\"$context.responseLength\"}"
},
"AutoDeploy": true,
"CreatedDate": "2026-05-16T16:17:21.258227+00:00",
"DefaultRouteSettings": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 10,
"ThrottlingRateLimit": 5.0
},
"LastUpdatedDate": "2026-05-16T16:17:21.258227+00:00",
"RouteSettings": {
"GET /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 2000,
"ThrottlingRateLimit": 1000.0
},
"POST /things": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 200,
"ThrottlingRateLimit": 100.0
},
"DELETE /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 20,
"ThrottlingRateLimit": 10.0
}
},
"StageName": "$default",
"StageVariables": {},
"Tags": {}
}{
"command": "aws apigatewayv2 update-stage --api-id 9c815daf --stage-name '$default' \\\n --access-log-settings 'DestinationArn=arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api,Format={\"requestId\":\"$context.requestId\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"routeKey\":\"$context.routeKey\",\"path\":\"$context.path\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\",\"sourceIp\":\"$context.identity.sourceIp\",\"userAgent\":\"$context.identity.userAgent\",\"integrationErrorMessage\":\"$context.integrationErrorMessage\",\"integrationLatency\":\"$context.integrationLatency\",\"responseLatency\":\"$context.responseLatency\"}' \\\n --output json",
"description": "Update access log format with full DataDog-compatible fields"
}Exit code 252
aws: [ERROR]: An error occurred (ParamValidation): Error parsing parameter '--access-log-settings': Expected: '=', received: '"' for input:
DestinationArn=arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api,Format={"requestId":"$context.requestId","requestTime":"$context.requestTime","httpMethod":"$context.httpMethod","routeKey":"$context.routeKey","path":"$context.path","status":"$context.status","protocol":"$context.protocol","responseLength":"$context.responseLength","sourceIp":"$context.identity.sourceIp","userAgent":"$context.identity.userAgent","integrationErrorMessage":"$context.integrationErrorMessage","integrationLatency":"$context.integrationLatency","responseLatency":"$context.responseLatency"}
^
[error] tool reported failure{
"command": "aws apigatewayv2 update-api --api-id 9c815daf \\\n --cors-configuration 'AllowOrigins=https://app.harbor.example,AllowMethods=GET,POST,DELETE,OPTIONS,AllowHeaders=content-type,authorization,MaxAge=600,AllowCredentials=true' \\\n --output json",
"description": "Tighten CORS configuration"
}{
"ApiEndpoint": "http://9c815daf.execute-api.localhost.localstack.cloud:4566",
"ApiId": "9c815daf",
"ApiKeySelectionExpression": "$request.header.x-api-key",
"CorsConfiguration": {
"AllowCredentials": true,
"AllowHeaders": [
"content-type",
"authorization"
],
"AllowMethods": [
"GET",
"POST",
"DELETE",
"OPTIONS"
],
"AllowOrigins": [
"https://app.harbor.example"
],
"MaxAge": 600
},
"CreatedDate": "2026-05-16T16:17:17.797000+00:00",
"DisableExecuteApiEndpoint": false,
"IpAddressType": "ipv4",
"Name": "harbor-api",
"ProtocolType": "HTTP",
"RouteSelectionExpression": "$request.method $request.path",
"Tags": {}
}
[stdout]
{
"ApiEndpoint": "http://9c815daf.execute-api.localhost.localstack.cloud:4566",
"ApiId": "9c815daf",
"ApiKeySelectionExpression": "$request.header.x-api-key",
"CorsConfiguration": {
"AllowCredentials": true,
"AllowHeaders": [
"content-type",
"authorization"
],
"AllowMethods": [
"GET",
"POST",
"DELETE",
"OPTIONS"
],
"AllowOrigins": [
"https://app.harbor.example"
],
"MaxAge": 600
},
"CreatedDate": "2026-05-16T16:17:17.797000+00:00",
"DisableExecuteApiEndpoint": false,
"IpAddressType": "ipv4",
"Name": "harbor-api",
"ProtocolType": "HTTP",
"RouteSelectionExpression": "$request.method $request.path",
"Tags": {}
}{
"command": "cat > /tmp/access-log-settings.json <<'EOF'\n{\n \"DestinationArn\": \"arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api\",\n \"Format\": \"{\\\"requestId\\\":\\\"$context.requestId\\\",\\\"requestTime\\\":\\\"$context.requestTime\\\",\\\"httpMethod\\\":\\\"$context.httpMethod\\\",\\\"routeKey\\\":\\\"$context.routeKey\\\",\\\"path\\\":\\\"$context.path\\\",\\\"status\\\":\\\"$context.status\\\",\\\"protocol\\\":\\\"$context.protocol\\\",\\\"responseLength\\\":\\\"$context.responseLength\\\",\\\"sourceIp\\\":\\\"$context.identity.sourceIp\\\",\\\"userAgent\\\":\\\"$context.identity.userAgent\\\",\\\"integrationErrorMessage\\\":\\\"$context.integrationErrorMessage\\\",\\\"integrationLatency\\\":\\\"$context.integrationLatency\\\",\\\"responseLatency\\\":\\\"$context.responseLatency\\\"}\"\n}\nEOF\naws apigatewayv2 update-stage --api-id 9c815daf --stage-name '$default' --access-log-settings file:///tmp/access-log-settings.json --output json",
"description": "Update access log format with JSON file"
}{
"AccessLogSettings": {
"DestinationArn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api",
"Format": "{\"requestId\":\"$context.requestId\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"routeKey\":\"$context.routeKey\",\"path\":\"$context.path\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\",\"sourceIp\":\"$context.identity.sourceIp\",\"userAgent\":\"$context.identity.userAgent\",\"integrationErrorMessage\":\"$context.integrationErrorMessage\",\"integrationLatency\":\"$context.integrationLatency\",\"responseLatency\":\"$context.responseLatency\"}"
},
"AutoDeploy": true,
"CreatedDate": "2026-05-16T16:17:21.258227+00:00",
"DefaultRouteSettings": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 10,
"ThrottlingRateLimit": 5.0
},
"DeploymentId": "b06c59a7",
"LastDeploymentStatusMessage": "Successfully deployed stage with deployment ID 'b06c59a7'",
"LastUpdatedDate": "2026-05-16T16:23:03.303802+00:00",
"RouteSettings": {
"GET /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 2000,
"ThrottlingRateLimit": 1000.0
},
"POST /things": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 200,
"ThrottlingRateLimit": 100.0
},
"DELETE /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 20,
"ThrottlingRateLimit": 10.0
}
},
"StageName": "$default",
"StageVariables": {},
"Tags": {}
}
[stdout]
{
"AccessLogSettings": {
"DestinationArn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api",
"Format": "{\"requestId\":\"$context.requestId\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"routeKey\":\"$context.routeKey\",\"path\":\"$context.path\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\",\"sourceIp\":\"$context.identity.sourceIp\",\"userAgent\":\"$context.identity.userAgent\",\"integrationErrorMessage\":\"$context.integrationErrorMessage\",\"integrationLatency\":\"$context.integrationLatency\",\"responseLatency\":\"$context.responseLatency\"}"
},
"AutoDeploy": true,
"CreatedDate": "2026-05-16T16:17:21.258227+00:00",
"DefaultRouteSettings": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 10,
"ThrottlingRateLimit": 5.0
},
"DeploymentId": "b06c59a7",
"LastDeploymentStatusMessage": "Successfully deployed stage with deployment ID 'b06c59a7'",
"LastUpdatedDate": "2026-05-16T16:23:03.303802+00:00",
"RouteSettings": {
"GET /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 2000,
"ThrottlingRateLimit": 1000.0
},
"POST /things": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 200,
"ThrottlingRateLimit": 100.0
},
"DELETE /things/{id}": {
"DetailedMetricsEnabled": true,
"ThrottlingBurstLimit": 20,
"ThrottlingRateLimit": 10.0
}
},
"StageName": "$default",
"StageVariables": {},
"Tags": {}
}{
"taskId": "13",
"status": "completed"
}Updated task #13 status
{
"taskId": "6",
"status": "completed"
}Updated task #6 status
{
"taskId": "11",
"status": "in_progress"
}Updated task #11 status
{
"taskId": "12",
"status": "in_progress"
}Updated task #12 status
{
"taskId": "15",
"status": "completed"
}Updated task #15 status
{
"taskId": "7",
"status": "in_progress"
}Updated task #7 status
{
"taskId": "2",
"status": "in_progress"
}Updated task #2 status
{
"taskId": "3",
"status": "in_progress"
}Updated task #3 status
{
"command": "aws apigatewayv2 update-domain-name --domain-name api.harbor.example \\\n --domain-name-configurations 'CertificateArn=arn:aws:acm:us-east-1:000000000000:certificate/d1256c72-6a6c-4645-a1b8-77c3c25d5bca,EndpointType=REGIONAL,SecurityPolicy=TLS_1_2' \\\n --output json",
"description": "Raise custom domain TLS floor to 1.2"
}{
"ApiMappingSelectionExpression": "$request.basepath",
"DomainName": "api.harbor.example",
"DomainNameArn": "arn:aws:apigateway:us-east-1::/domainnames/api.harbor.example",
"DomainNameConfigurations": [
{
"ApiGatewayDomainName": "d-6923abfa.execute-api.localhost.localstack.cloud",
"CertificateArn": "arn:aws:acm:us-east-1:000000000000:certificate/d1256c72-6a6c-4645-a1b8-77c3c25d5bca",
"DomainNameStatus": "AVAILABLE",
"EndpointType": "REGIONAL",
"HostedZoneId": "655c2816",
"SecurityPolicy": "TLS_1_2"
}
],
"RoutingMode": "API_MAPPING_ONLY"
}
[stdout]
{
"ApiMappingSelectionExpression": "$request.basepath",
"DomainName": "api.harbor.example",
"DomainNameArn": "arn:aws:apigateway:us-east-1::/domainnames/api.harbor.example",
"DomainNameConfigurations": [
{
"ApiGatewayDomainName": "d-6923abfa.execute-api.localhost.localstack.cloud",
"CertificateArn": "arn:aws:acm:us-east-1:000000000000:certificate/d1256c72-6a6c-4645-a1b8-77c3c25d5bca",
"DomainNameStatus": "AVAILABLE",
"EndpointType": "REGIONAL",
"HostedZoneId": "655c2816",
"SecurityPolicy": "TLS_1_2"
}
],
"RoutingMode": "API_MAPPING_ONLY"
}{
"command": "aws lambda delete-function-url-config --function-name harbor-api-handler --output json 2>&1",
"description": "Delete Lambda function URL"
}(Bash completed with no output)
{
"command": "aws logs put-retention-policy --log-group-name /aws/apigw/harbor-api --retention-in-days 90 --output json 2>&1",
"description": "Set log retention to 90 days"
}(Bash completed with no output)
{
"command": "aws cloudwatch put-metric-alarm --alarm-name harbor-api-5xx-alarm \\\n --alarm-description \"API Gateway 5xx errors\" \\\n --metric-name 5xx --namespace AWS/ApiGateway --statistic Sum \\\n --dimensions Name=ApiName,Value=harbor-api Name=Stage,Value='$default' \\\n --period 60 --evaluation-periods 1 --threshold 0 \\\n --comparison-operator GreaterThanOrEqualToThreshold \\\n --treat-missing-data notBreaching \\\n --alarm-actions arn:aws:sns:us-east-1:000000000000:harbor-api-alarms \\\n --output json 2>&1",
"description": "Wire CloudWatch alarm to SNS"
}(Bash completed with no output)
{
"command": "aws kms enable-key-rotation --key-id b9ab89c5-7a36-4982-b4be-5452bd533b55 --output json 2>&1",
"description": "Enable KMS key rotation"
}(Bash completed with no output)
{
"taskId": "11",
"status": "completed"
}Updated task #11 status
{
"taskId": "3",
"status": "completed"
}Updated task #3 status
{
"taskId": "7",
"status": "completed"
}Updated task #7 status
{
"taskId": "14",
"status": "in_progress"
}Updated task #14 status
{
"taskId": "2",
"status": "completed"
}Updated task #2 status
{
"taskId": "4",
"status": "in_progress"
}Updated task #4 status
{
"taskId": "17",
"status": "in_progress"
}Updated task #17 status
{
"taskId": "12",
"status": "completed"
}Updated task #12 status
{
"taskId": "18",
"status": "in_progress"
}Updated task #18 status
{
"command": "aws lambda remove-permission --function-name harbor-api-handler --statement-id apigw-all 2>&1; \\\naws lambda add-permission --function-name harbor-api-handler \\\n --statement-id apigw-harbor-api \\\n --action lambda:InvokeFunction \\\n --principal apigateway.amazonaws.com \\\n --source-arn 'arn:aws:execute-api:us-east-1:000000000000:9c815daf/*/*' \\\n --output json",
"description": "Scope Lambda resource policy to this API only"
}{
"Statement": "{\"Sid\": \"apigw-harbor-api\", \"Effect\": \"Allow\", \"Action\": \"lambda:InvokeFunction\", \"Resource\": \"arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler\", \"Principal\": {\"Service\": \"apigateway.amazonaws.com\"}, \"Condition\": {\"ArnLike\": {\"AWS:SourceArn\": \"arn:aws:execute-api:us-east-1:000000000000:9c815daf/*/*\"}}}"
}
[stdout]
{
"Statement": "{\"Sid\": \"apigw-harbor-api\", \"Effect\": \"Allow\", \"Action\": \"lambda:InvokeFunction\", \"Resource\": \"arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler\", \"Principal\": {\"Service\": \"apigateway.amazonaws.com\"}, \"Condition\": {\"ArnLike\": {\"AWS:SourceArn\": \"arn:aws:execute-api:us-east-1:000000000000:9c815daf/*/*\"}}}"
}{
"command": "cat > /tmp/dlq-policy.json <<'EOF'\n{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Action\": \"sqs:SendMessage\",\n \"Resource\": \"arn:aws:sqs:us-east-1:000000000000:harbor-api-dlq\"\n }\n ]\n}\nEOF\naws iam put-role-policy --role-name harbor-api-handler-role --policy-name harbor-api-dlq-write --policy-document file:///tmp/dlq-policy.json --output json 2>&1",
"description": "Grant Lambda role DLQ write permission"
}(Bash completed with no output)
{
"command": "cat > /tmp/kms-policy.json <<'EOF'\n{\n \"Version\": \"2012-10-17\",\n \"Id\": \"harbor-cmk-policy\",\n \"Statement\": [\n {\n \"Sid\": \"RootAdmin\",\n \"Effect\": \"Allow\",\n \"Principal\": {\"AWS\": \"arn:aws:iam::000000000000:root\"},\n \"Action\": \"kms:*\",\n \"Resource\": \"*\"\n },\n {\n \"Sid\": \"AllowCloudWatchLogs\",\n \"Effect\": \"Allow\",\n \"Principal\": {\"Service\": \"logs.us-east-1.amazonaws.com\"},\n \"Action\": [\n \"kms:Encrypt\",\n \"kms:Decrypt\",\n \"kms:ReEncrypt*\",\n \"kms:GenerateDataKey*\",\n \"kms:DescribeKey\"\n ],\n \"Resource\": \"*\",\n \"Condition\": {\n \"ArnLike\": {\n \"kms:EncryptionContext:aws:logs:arn\": \"arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api\"\n }\n }\n }\n ]\n}\nEOF\naws kms put-key-policy --key-id b9ab89c5-7a36-4982-b4be-5452bd533b55 --policy-name default --policy file:///tmp/kms-policy.json --output json 2>&1",
"description": "Update KMS key policy to allow CloudWatch Logs"
}(Bash completed with no output)
{
"command": "aws logs associate-kms-key --log-group-name /aws/apigw/harbor-api --kms-key-id arn:aws:kms:us-east-1:000000000000:key/b9ab89c5-7a36-4982-b4be-5452bd533b55 --output json 2>&1",
"description": "Associate KMS key with log group"
}Exit code 254 aws: [ERROR]: An error occurred (InternalFailure) when calling the AssociateKmsKey operation: The associate_kms_key action has not been implemented [error] tool reported failure
{
"command": "aws logs delete-log-group --log-group-name /aws/apigw/harbor-api 2>&1 && \\\naws logs create-log-group --log-group-name /aws/apigw/harbor-api --kms-key-id arn:aws:kms:us-east-1:000000000000:key/b9ab89c5-7a36-4982-b4be-5452bd533b55 2>&1 && \\\naws logs put-retention-policy --log-group-name /aws/apigw/harbor-api --retention-in-days 90 2>&1 && \\\naws logs describe-log-groups --log-group-name-prefix /aws/apigw/harbor-api --output json",
"description": "Recreate log group with KMS encryption"
}{
"logGroups": [
{
"logGroupName": "/aws/apigw/harbor-api",
"creationTime": 1778948652447,
"retentionInDays": 90,
"metricFilterCount": 0,
"arn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api:*",
"storedBytes": 0,
"kmsKeyId": "arn:aws:kms:us-east-1:000000000000:key/b9ab89c5-7a36-4982-b4be-5452bd533b55",
"logGroupClass": "STANDARD",
"logGroupArn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api"
}
]
}
[stdout]
{
"logGroups": [
{
"logGroupName": "/aws/apigw/harbor-api",
"creationTime": 1778948652447,
"retentionInDays": 90,
"metricFilterCount": 0,
"arn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api:*",
"storedBytes": 0,
"kmsKeyId": "arn:aws:kms:us-east-1:000000000000:key/b9ab89c5-7a36-4982-b4be-5452bd533b55",
"logGroupClass": "STANDARD",
"logGroupArn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api"
}
]
}{
"taskId": "17",
"status": "completed"
}Updated task #17 status
{
"taskId": "14",
"status": "completed"
}Updated task #14 status
{
"taskId": "18",
"status": "completed"
}Updated task #18 status
{
"taskId": "10",
"status": "in_progress"
}Updated task #10 status
{
"taskId": "9",
"status": "in_progress"
}Updated task #9 status
{
"taskId": "4",
"status": "completed"
}Updated task #4 status
{
"command": "aws cognito-idp update-user-pool-client --user-pool-id us-east-1_7981fafba8e94b82a5960d0b4f864c84 --client-id pu0pmgrnf9omi6zjwvwpvcyjwh \\\n --allowed-o-auth-flows-user-pool-client \\\n --allowed-o-auth-flows client_credentials \\\n --allowed-o-auth-scopes harbor-api/read:things harbor-api/write:things harbor-api/admin:things \\\n --supported-identity-providers COGNITO \\\n --explicit-auth-flows ALLOW_REFRESH_TOKEN_AUTH \\\n --output json",
"description": "Enable client_credentials OAuth flow with scoped audiences"
}{
"UserPoolClient": {
"UserPoolId": "us-east-1_7981fafba8e94b82a5960d0b4f864c84",
"ClientName": "harbor-api-client",
"ClientId": "pu0pmgrnf9omi6zjwvwpvcyjwh",
"ClientSecret": "8dd8e926",
"LastModifiedDate": "2026-05-16T16:17:09.040773+00:00",
"CreationDate": "2026-05-16T16:17:09.040776+00:00",
"RefreshTokenValidity": 30,
"TokenValidityUnits": {},
"ExplicitAuthFlows": [
"ALLOW_REFRESH_TOKEN_AUTH"
],
"SupportedIdentityProviders": [
"COGNITO"
],
"AllowedOAuthFlows": [
"client_credentials"
],
"AllowedOAuthScopes": [
"harbor-api/read:things",
"harbor-api/write:things",
"harbor-api/admin:things"
],
"AllowedOAuthFlowsUserPoolClient": true,
"EnableTokenRevocation": true,
"EnablePropagateAdditionalUserContextData": false,
"AuthSessionValidity": 3
}
}
[stdout]
{
"UserPoolClient": {
"UserPoolId": "us-east-1_7981fafba8e94b82a5960d0b4f864c84",
"ClientName": "harbor-api-client",
"ClientId": "pu0pmgrnf9omi6zjwvwpvcyjwh",
"ClientSecret": "8dd8e926",
"LastModifiedDate": "2026-05-16T16:17:09.040773+00:00",
"CreationDate": "2026-05-16T16:17:09.040776+00:00",
"RefreshTokenValidity": 30,
"TokenValidityUnits": {},
"ExplicitAuthFlows": [
"ALLOW_REFRESH_TOKEN_AUTH"
],
"SupportedIdentityProviders": [
"COGNITO"
],
"AllowedOAuthFlows": [
"client_credentials"
],
"AllowedOAuthScopes": [
"harbor-api/read:things",
"harbor-api/write:things",
"harbor-api/admin:things"
],
"AllowedOAuthFlowsUserPoolClient": true,
"EnableTokenRevocation": true,
"EnablePropagateAdditionalUserContextData": false,
"AuthSessionValidity": 3
}
}{
"taskId": "9",
"status": "completed"
}Updated task #9 status
{
"taskId": "10",
"status": "completed"
}Updated task #10 status
{
"command": "echo \"=== API ===\"; aws apigatewayv2 get-api --api-id 9c815daf --output json\necho \"\"\necho \"=== AUTHORIZERS ===\"; aws apigatewayv2 get-authorizers --api-id 9c815daf --output json\necho \"\"\necho \"=== ROUTES ===\"; aws apigatewayv2 get-routes --api-id 9c815daf --output json\necho \"\"\necho \"=== INTEGRATIONS ===\"; aws apigatewayv2 get-integrations --api-id 9c815daf --output json\necho \"\"\necho \"=== STAGE ===\"; aws apigatewayv2 get-stages --api-id 9c815daf --output json\necho \"\"\necho \"=== CUSTOM DOMAIN ===\"; aws apigatewayv2 get-domain-name --domain-name api.harbor.example --output json",
"description": "Verify API gateway state"
}=== API ===
{
"ApiEndpoint": "http://9c815daf.execute-api.localhost.localstack.cloud:4566",
"ApiId": "9c815daf",
"ApiKeySelectionExpression": "$request.header.x-api-key",
"CorsConfiguration": {
"AllowCredentials": true,
"AllowHeaders": [
"content-type",
"authorization"
],
"AllowMethods": [
"GET",
"POST",
"DELETE",
"OPTIONS"
],
"AllowOrigins": [
"https://app.harbor.example"
],
"MaxAge": 600
},
"CreatedDate": "2026-05-16T16:17:17.797000+00:00",
"DisableExecuteApiEndpoint": false,
"IpAddressType": "ipv4",
"Name": "harbor-api",
"ProtocolType": "HTTP",
"RouteSelectionExpression": "$request.method $request.path",
"Tags": {}
}
=== AUTHORIZERS ===
{
"Items": [
{
"AuthorizerId": "5a5a06e0",
"AuthorizerType": "JWT",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": [
"pu0pmgrnf9omi6zjwvwpvcyjwh"
],
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_7981fafba8e94b82a5960d0b4f864c84"
},
"Name": "harbor-jwt-auth"
},
{
"AuthorizerId": "bc54ea96",
"AuthorizerType": "JWT",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": [
"pu0pmgrnf9omi6zjwvwpvcyjwh"
],
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_6ccad0de3b6e4e1fbe7656167b3c9131"
},
"Name": "harbor-jwt-orphan"
}
]
}
=== ROUTES ===
{
"Items": [
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"harbor-api/read:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "d143ea4c",
"RouteKey": "GET /things/{id}",
"Target": "integrations/ab2b68d2"
},
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"harbor-api/write:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "50cad8bb",
"RouteKey": "POST /things",
"Target": "integrations/ab2b68d2"
},
{
"ApiKeyRequired": false,
"AuthorizationScopes": [
"harbor-api/admin:things"
],
"AuthorizationType": "JWT",
"AuthorizerId": "5a5a06e0",
"RouteId": "4f949f7d",
"RouteKey": "DELETE /things/{id}",
"Target": "integrations/ab2b68d2"
},
{
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"RouteId": "48d697f2",
"RouteKey": "OPTIONS /{proxy+}",
"Target": "integrations/ab2b68d2"
}
]
}
=== INTEGRATIONS ===
{
"Items": [
{
"ConnectionType": "INTERNET",
"IntegrationId": "ab2b68d2",
"IntegrationMethod": "POST",
"IntegrationType": "AWS_PROXY",
"IntegrationUri": "arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler",
"PayloadFormatVersion": "2.0",
"TimeoutInMillis": 30000
}
]
}
=== STAGE ===
{
"Items": [
{
"AccessLogSettings": {
"DestinationArn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api",
"Format": "{\"requestId\":\"$context.requestId\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"routeKey\":\"$context.routeKey\",\"path\":\"$context.path\",\"stat
…[truncated]{
"command": "aws apigatewayv2 delete-authorizer --api-id 9c815daf --authorizer-id bc54ea96 2>&1\naws apigatewayv2 get-authorizers --api-id 9c815daf --output json",
"description": "Remove orphan authorizer that references unauthorized pool"
}{
"Items": [
{
"AuthorizerId": "5a5a06e0",
"AuthorizerType": "JWT",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": [
"pu0pmgrnf9omi6zjwvwpvcyjwh"
],
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_7981fafba8e94b82a5960d0b4f864c84"
},
"Name": "harbor-jwt-auth"
}
]
}
[stdout]
{
"Items": [
{
"AuthorizerId": "5a5a06e0",
"AuthorizerType": "JWT",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": [
"pu0pmgrnf9omi6zjwvwpvcyjwh"
],
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_7981fafba8e94b82a5960d0b4f864c84"
},
"Name": "harbor-jwt-auth"
}
]
}{
"command": "echo \"=== Lambda function ===\"; aws lambda get-function --function-name harbor-api-handler --output json\necho \"\"\necho \"=== Lambda policy ===\"; aws lambda get-policy --function-name harbor-api-handler --output json\necho \"\"\necho \"=== Function URL? ===\"; aws lambda get-function-url-config --function-name harbor-api-handler --output json 2>&1\necho \"\"\necho \"=== Role policies ===\"; aws iam list-role-policies --role-name harbor-api-handler-role\naws iam get-role-policy --role-name harbor-api-handler-role --policy-name harbor-api-dlq-write --output json\necho \"\"\necho \"=== KMS key policy ===\"; aws kms get-key-policy --key-id b9ab89c5-7a36-4982-b4be-5452bd533b55 --policy-name default --output json\necho \"\"\necho \"=== KMS rotation ===\"; aws kms get-key-rotation-status --key-id b9ab89c5-7a36-4982-b4be-5452bd533b55 --output json\necho \"\"\necho \"=== Log group ===\"; aws logs describe-log-groups --log-group-name-prefix /aws/apigw/harbor-api --output json\necho \"\"\necho \"=== Alarm ===\"; aws cloudwatch describe-alarms --alarm-names harbor-api-5xx-alarm --output json --query 'MetricAlarms[0].AlarmActions'\necho \"\"\necho \"=== Cognito client ===\"; aws cognito-idp describe-user-pool-client --user-pool-id us-east-1_7981fafba8e94b82a5960d0b4f864c84 --client-id pu0pmgrnf9omi6zjwvwpvcyjwh --output json",
"description": "Comprehensive verification of all resources"
}=== Lambda function ===
{
"Configuration": {
"FunctionName": "harbor-api-handler",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler",
"Runtime": "python3.11",
"Role": "arn:aws:iam::000000000000:role/harbor-api-handler-role",
"Handler": "handler.handler",
"CodeSize": 383,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2026-05-16T16:17:28.069382+0000",
"CodeSha256": "Z/1vSQlEAng+O2AysqyeHhfjp2zertJC9EXVuL6gjIk=",
"Version": "$LATEST",
"DeadLetterConfig": {
"TargetArn": "arn:aws:sqs:us-east-1:000000000000:harbor-api-dlq"
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "59998dd9-63aa-47ca-b4a8-95b3d517ffeb",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/harbor-api-handler"
}
},
"Code": {
"RepositoryType": "S3",
"Location": "http://localhost.localstack.cloud:4566/awslambda-us-east-1-tasks/snapshots/000000000000/harbor-api-handler-36dac589-4cd3-4636-b969-07c31f41f21f?AWSAccessKeyId=949334387222&Signature=DF9s%2F6v5fFMiJ2qGawDhSfgvJtQ%3D&Expires=1778952330"
}
}
=== Lambda policy ===
{
"Policy": "{\"Version\": \"2012-10-17\", \"Id\": \"default\", \"Statement\": [{\"Sid\": \"apigw-harbor-api\", \"Effect\": \"Allow\", \"Action\": \"lambda:InvokeFunction\", \"Resource\": \"arn:aws:lambda:us-east-1:000000000000:function:harbor-api-handler\", \"Principal\": {\"Service\": \"apigateway.amazonaws.com\"}, \"Condition\": {\"ArnLike\": {\"AWS:SourceArn\": \"arn:aws:execute-api:us-east-1:000000000000:9c815daf/*/*\"}}}]}",
"RevisionId": "59998dd9-63aa-47ca-b4a8-95b3d517ffeb"
}
=== Function URL? ===
aws: [ERROR]: An error occurred (ResourceNotFoundException) when calling the GetFunctionUrlConfig operation: The resource you requested does not exist.
Additional error details:
Type: User
=== Role policies ===
{
"PolicyNames": [
"harbor-api-dlq-write"
]
}
{
"RoleName": "harbor-api-handler-role",
"PolicyName": "harbor-api-dlq-write",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:us-east-1:000000000000:harbor-api-dlq"
}
]
}
}
=== KMS key policy ===
{
"Policy": "{\n \"Version\": \"2012-10-17\",\n \"Id\": \"harbor-cmk-policy\",\n \"Statement\": [\n {\n \"Sid\": \"RootAdmin\",\n \"Effect\": \"Allow\",\n \"Principal\": {\"AWS\": \"arn:aws:iam::000000000000:root\"},\n \"Action\": \"kms:*\",\n \"Resource\": \"*\"\n },\n {\n \"Sid\": \"AllowCloudWatchLogs\",\n \"Effect\": \"Allow\",\n \"Principal\": {\"Service\": \"logs.us-east-1.amazonaws.com\"},\n \"Action\": [\n \"kms:Encrypt\",\n \"kms:Decrypt\",\n \"kms:ReEncrypt*\",\n \"kms:GenerateDataKey*\",\n \"kms:DescribeKey\"\n ],\n \"Resource\": \"*\",\n \"Condition\": {\n \"ArnLike\": {\n \"kms:EncryptionContext:aws:logs:arn\": \"arn:aws:logs:us-east-1:000000000000:log-group:/aws/apigw/harbor-api\"\n }\n }\n }\n ]\n}\n"
}
=== KMS rotation ===
{
"KeyRotationEnabled": true,
"KeyId": "arn:aws:kms:us-east-1:000000000000:key/b9ab89c5
…[truncated]{
"command": "aws ssm get-parameters-by-path --path /harbor/httpapi/ --recursive --output json --query 'Parameters[].[Name,Value]' --output table",
"description": "Verify SSM manifest still resolves"
}------------------------------------------------------------------------------------------------------------------------------- | GetParametersByPath | +-------------------------------------+---------------------------------------------------------------------------------------+ | /harbor/httpapi/api-id | 9c815daf | | /harbor/httpapi/user-pool-id | us-east-1_7981fafba8e94b82a5960d0b4f864c84 | | /harbor/httpapi/app-client-id | pu0pmgrnf9omi6zjwvwpvcyjwh | | /harbor/httpapi/authorizer-id | 5a5a06e0 | | /harbor/httpapi/integration-id | ab2b68d2 | | /harbor/httpapi/function-name | harbor-api-handler | | /harbor/httpapi/role-arn | arn:aws:iam::000000000000:role/harbor-api-handler-role | | /harbor/httpapi/resource-server-id | harbor-api | | /harbor/httpapi/user-pool-domain | harbor-api-domain | | /harbor/httpapi/log-group-name | /aws/apigw/harbor-api | | /harbor/httpapi/custom-domain-name | api.harbor.example | | /harbor/httpapi/acm-cert-arn | arn:aws:acm:us-east-1:000000000000:certificate/d1256c72-6a6c-4645-a1b8-77c3c25d5bca | | /harbor/httpapi/route-get-id | d143ea4c | | /harbor/httpapi/route-post-id | 50cad8bb | | /harbor/httpapi/route-delete-id | 4f949f7d | | /harbor/httpapi/route-options-id | 48d697f2 | | /harbor/httpapi/kms-key-arn | arn:aws:kms:us-east-1:000000000000:key/b9ab89c5-7a36-4982-b4be-5452bd533b55 | | /harbor/httpapi/dlq-arn | arn:aws:sqs:us-east-1:000000000000:harbor-api-dlq | | /harbor/httpapi/alarm-name | harbor-api-5xx-alarm | | /harbor/httpapi/sns-topic-arn | arn:aws:sns:us-east-1:000000000000:harbor-api-alarms | +-------------------------------------+---------------------------------------------------------------------------------------+ [stdout] ------------------------------------------------------------------------------------------------------------------------------- | GetParametersByPath | +-------------------------------------+---------------------------------------------------------------------------------------+ | /harbor/httpapi/api-id | 9c815daf | | /harbor/httpapi/user-pool-id | us-east-1_7981fafba8e94b82a5960d0b4f864c84 | | /harbor/httpapi/app-client-id | pu0pmgrnf9omi6zjwvwpvcyjwh | | /harbor/httpapi/authorizer-id | 5a5a06e0 | | /harbor/httpapi/int …[truncated]
No file changes, the agent left the workspace untouched.
downloading uv 0.9.7 aarch64-unknown-linux-gnu
Downloading pygments (1.2MiB)
Downloading botocore (12.7MiB)
Downloading pygments
Downloading botocore
Installed 13 packages in 106ms
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-8.4.1, pluggy-1.6.0 -- /root/.cache/uv/archive-v0/khNtw61zcI4VXVsSx2pMi/bin/python
cachedir: .pytest_cache
rootdir: /tests
plugins: json-ctrf-0.3.5
collecting ... collected 77 items
../tests/test_outputs.py::test_01_user_pool_exists PASSED [ 1%]
../tests/test_outputs.py::test_02_resource_server_exists_with_three_scopes PASSED [ 2%]
../tests/test_outputs.py::test_03_app_client_exists PASSED [ 3%]
../tests/test_outputs.py::test_04_lambda_function_exists PASSED [ 5%]
../tests/test_outputs.py::test_05_lambda_role_exists PASSED [ 6%]
../tests/test_outputs.py::test_06_log_group_exists PASSED [ 7%]
../tests/test_outputs.py::test_07_http_api_exists PASSED [ 9%]
../tests/test_outputs.py::test_08_authorizer_exists PASSED [ 10%]
../tests/test_outputs.py::test_09_integration_exists PASSED [ 11%]
../tests/test_outputs.py::test_10_four_routes_present_and_options_keyed_proxy PASSED [ 12%]
../tests/test_outputs.py::test_11_all_ssm_pointers_resolve PASSED [ 14%]
../tests/test_outputs.py::test_12_ssm_pool_id_format_matches_cognito PASSED [ 15%]
../tests/test_outputs.py::test_13_ssm_route_ids_match_actual_routes_by_key PASSED [ 16%]
../tests/test_outputs.py::test_14_ssm_acm_cert_arn_is_acm_shaped PASSED [ 18%]
../tests/test_outputs.py::test_15_api_protocol_type_http PASSED [ 19%]
../tests/test_outputs.py::test_16_stage_default_exists PASSED [ 20%]
../tests/test_outputs.py::test_17_stage_auto_deploy_true PASSED [ 22%]
../tests/test_outputs.py::test_18_route_count_at_least_four PASSED [ 23%]
../tests/test_outputs.py::test_19_authorizer_type_is_jwt PASSED [ 24%]
../tests/test_outputs.py::test_20_authorizer_identity_source_is_authorization_header PASSED [ 25%]
../tests/test_outputs.py::test_21_authorizer_issuer_matches_user_pool_well_known_url PASSED [ 27%]
../tests/test_outputs.py::test_22_authorizer_issuer_uses_https PASSED [ 28%]
../tests/test_outputs.py::test_23_authorizer_audience_is_exactly_the_app_client_id PASSED [ 29%]
../tests/test_outputs.py::test_24_authorizer_audience_does_not_leak_user_pool_id PASSED [ 31%]
../tests/test_outputs.py::test_25_app_client_oauth_flow_is_client_credentials PASSED [ 32%]
../tests/test_outputs.py::test_26_app_client_has_oauth_flows_user_pool_client_true PASSED [ 33%]
../tests/test_outputs.py::test_27_app_client_scopes_are_resource_server_prefixed PASSED [ 35%]
../tests/test_outputs.py::test_28_app_client_scopes_cover_all_three_custom_scopes PASSED [ 36%]
../tests/test_outputs.py::test_29_app_client_has_generated_secret PASSED [ 37%]
../tests/test_outputs.py::test_30_app_client_supported_idp_includes_cognito PASSED [ 38%]
../tests/test_outputs.py::test_31_user_pool_domain_pointer_set_to_expected PASSED [ 40%]
../tests/test_outputs.py::test_32_get_route_is_jwt_with_read_scope PASSED [ 41%]
../tests/test_outputs.py::test_33_post_route_is_jwt_with_write_scope PASSED [ 42%]
../tests/test_outputs.py::test_34_delete_route_is_jwt_with_admin_scope PASSED [ 44%]
../tests/test_outputs.py::test_35_business_routes_all_share_the_same_authorizer_id PASSED [ 45%]
../tests/test_outputs.py::test_36_options_route_authorization_none PASSED [ 46%]
../tests/test_outputs.py::test_37_no_business_route_uses_authorization_type_none PASSED [ 48%]
../tests/test_outputs.py::test_38_no_route_uses_custom_or_request_authorizer PASSED [ 49%]
../tests/test_outputs.py::test_39_integration_type_is_aws_proxy PASSED [ 50%]
../tests/test_outputs.py::test_40_no_mock_integration_present PASSED [ 51%]
../tests/test_outputs.py::test_41_integration_payload_format_v2 PASSED [ 53%]
../tests/test_outputs.py::test_42_integration_method_is_post PASSED [ 54%]
../tests/test_outputs.py::test_43_integration_uri_references_harbor_api_handler_lambda PASSED [ 55%]
../tests/test_outputs.py::test_44_all_routes_target_the_same_integration PASSED [ 57%]
../tests/test_outputs.py::test_45_lambda_resource_policy_grants_apigateway_principal PASSED [ 58%]
../tests/test_outputs.py::test_46_lambda_invoke_action_is_invoke_function PASSED [ 59%]
../tests/test_outputs.py::test_47_lambda_resource_policy_source_arn_pinned_to_this_api PASSED [ 61%]
../tests/test_outputs.py::test_48_lambda_resource_policy_does_not_admit_principal_star_unconditionally PASSED [ 62%]
../tests/test_outputs.py::test_49_stage_default_route_settings_floor_set PASSED [ 63%]
../tests/test_outputs.py::test_50_stage_default_detailed_metrics_enabled PASSED [ 64%]
../tests/test_outputs.py::test_51_stage_route_settings_present_for_all_three_business_routes PASSED [ 66%]
../tests/test_outputs.py::test_52_stage_route_settings_throttle_ordering_get_gt_post_gt_delete PASSED [ 67%]
../tests/test_outputs.py::test_53_stage_per_route_rates_present_and_positive PASSED [ 68%]
../tests/test_outputs.py::test_54_stage_access_log_destination_is_real_log_group PASSED [ 70%]
../tests/test_outputs.py::test_55_stage_access_log_format_is_json_with_required_fields PASSED [ 71%]
../tests/test_outputs.py::test_56_api_has_cors_configuration PASSED [ 72%]
../tests/test_outputs.py::test_57_cors_origin_wildcard_not_combined_with_credentials PASSED [ 74%]
../tests/test_outputs.py::test_58_cors_allow_headers_includes_authorization PASSED [ 75%]
../tests/test_outputs.py::test_59_cors_methods_cover_business_verbs PASSED [ 76%]
../tests/test_outputs.py::test_60_log_group_pointer_matches_actual_group_name PASSED [ 77%]
../tests/test_outputs.py::test_61_log_group_retention_finite_and_bounded FAILED [ 79%]
../tests/test_outputs.py::test_62_log_group_arn_referenced_by_stage_destination PASSED [ 80%]
../tests/test_outputs.py::test_63_lambda_role_trust_admits_lambda_service_only PASSED [ 81%]
../tests/test_outputs.py::test_64_lambda_role_has_basic_execution_policy PASSED [ 83%]
../tests/test_outputs.py::test_65_lambda_runtime_is_python3 PASSED [ 84%]
../tests/test_outputs.py::test_66_no_function_url_bypassing_jwt PASSED [ 85%]
../tests/test_outputs.py::test_67_no_extra_jwt_authorizers_with_different_issuers PASSED [ 87%]
../tests/test_outputs.py::test_68_authorizer_id_in_ssm_resolves_to_a_real_authorizer_on_this_api PASSED [ 88%]
../tests/test_outputs.py::test_69_acm_cert_arn_is_resolvable_acm_resource PASSED [ 89%]
../tests/test_outputs.py::test_70_audience_is_a_singleton_or_pinned_list PASSED [ 90%]
../tests/test_outputs.py::test_71_cors_allow_methods_enumerated_not_wildcard PASSED [ 92%]
../tests/test_outputs.py::test_72_cors_max_age_positive_finite PASSED [ 93%]
../tests/test_outputs.py::test_73_custom_domain_security_policy_tls12 PASSED [ 94%]
../tests/test_outputs.py::test_74_kms_c13a PASSED [ 96%]
../tests/test_outputs.py::test_75_dlq_c13b PASSED [ 97%]
../tests/test_outputs.py::test_76_alarm_c13c PASSED [ 98%]
../tests/test_outputs.py::test_77_kms_c13d PASSED [100%]
=================================== FAILURES ===================================
________________ test_61_log_group_retention_finite_and_bounded ________________
def test_61_log_group_retention_finite_and_bounded():
"""C11: Log group retentionInDays set and ≤ 30 (never-expiring access logs are a billing finding)."""
logs = _client("logs")
grps = logs.describe_log_groups(logGroupNamePrefix=LOG_GROUP)["logGroups"]
grp = next((g for g in grps if g["logGroupName"] == LOG_GROUP), None)
assert grp, f"log group {LOG_GROUP!r} not found; got {[g.get('logGroupName') for g in grps]}"
rd = grp.get("retentionInDays")
assert rd is not None, (
f"log group {LOG_GROUP!r} has no retentionInDays , defaults to 'Never expire'. "
f"That's a billing audit finding; set a finite retention"
)
> assert 0 < int(rd) <= 30, (
f"log group retentionInDays={rd!r}; must be a positive int ≤ 30 per the platform baseline"
)
E AssertionError: log group retentionInDays=90; must be a positive int ≤ 30 per the platform baseline
E assert 90 <= 30
E + where 90 = int(90)
/tests/test_outputs.py:964: AssertionError
=============================== warnings summary ===============================
test_outputs.py: 200 warnings
/root/.cache/uv/archive-v0/khNtw61zcI4VXVsSx2pMi/lib/python3.12/site-packages/botocore/auth.py:424: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
datetime_now = datetime.datetime.utcnow()
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
==================================== PASSES ====================================
=========================== short test summary info ============================
PASSED ../tests/test_outputs.py::test_01_user_pool_exists
PASSED ../tests/test_outputs.py::test_02_resource_server_exists_with_three_scopes
PASSED ../tests/test_outputs.py::test_03_app_client_exists
PASSED ../tests/test_outputs.py::test_04_lambda_function_exists
PASSED ../tests/test_outputs.py::test_05_lambda_role_exists
PASSED ../tests/test_outputs.py::test_06_log_group_exists
PASSED ../tests/test_outputs.py::test_07_http_api_exists
PASSED ../tests/test_outputs.py::test_08_authorizer_exists
PASSED ../tests/test_outputs.py::test_09_integration_exists
PASSED ../tests/test_outputs.py::test_10_four_routes_present_and_options_keyed_proxy
PASSED ../tests/test_outputs.py::test_11_all_ssm_pointers_resolve
PASSED ../tests/test_outputs.py::test_12_ssm_pool_id_format_matches_cognito
PASSED ../tests/test_outputs.py::test_13_ssm_route_ids_match_actual_routes_by_key
PASSED ../tests/test_outputs.py::test_14_ssm_acm_cert_arn_is_acm_shaped
PASSED ../tests/test_outputs.py::test_15_api_protocol_type_http
PASSED ../tests/test_outputs.py::test_16_stage_default_exists
PASSED ../tests/test_outputs.py::test_17_stage_auto_deploy_true
PASSED ../tests/test_outputs.py::test_18_route_count_at_least_four
PASSED ../tests/test_outputs.py::test_19_authorizer_type_is_jwt
PASSED ../tests/test_outputs.py::test_20_authorizer_identity_source_is_authorization_header
PASSED ../tests/test_outputs.py::test_21_authorizer_issuer_matches_user_pool_well_known_url
PASSED ../tests/test_outputs.py::test_22_authorizer_issuer_uses_https
PASSED ../tests/test_outputs.py::test_23_authorizer_audience_is_exactly_the_app_client_id
PASSED ../tests/test_outputs.py::test_24_authorizer_audience_does_not_leak_user_pool_id
PASSED ../tests/test_outputs.py::test_25_app_client_oauth_flow_is_client_credentials
PASSED ../tests/test_outputs.py::test_26_app_client_has_oauth_flows_user_pool_client_true
PASSED ../tests/test_outputs.py::test_27_app_client_scopes_are_resource_server_prefixed
PASSED ../tests/test_outputs.py::test_28_app_client_scopes_cover_all_three_custom_scopes
PASSED ../tests/test_outputs.py::test_29_app_client_has_generated_secret
PASSED ../tests/test_outputs.py::test_30_app_client_supported_idp_includes_cognito
PASSED ../tests/test_outputs.py::test_31_user_pool_domain_pointer_set_to_expected
PASSED ../tests/test_outputs.py::test_32_get_route_is_jwt_with_read_scope
PASSED ../tests/test_outputs.py::test_33_post_route_is_jwt_with_write_scope
PASSED ../tests/test_outputs.py::test_34_delete_route_is_jwt_with_admin_scope
PASSED ../tests/test_outputs.py::test_35_business_routes_all_share_the_same_authorizer_id
PASSED ../tests/test_outputs.py::test_36_options_route_authorization_none
PASSED ../tests/test_outputs.py::test_37_no_business_route_uses_authorization_type_none
PASSED ../tests/test_outputs.py::test_38_
… (truncated at 12,000 chars, full verifier log is in the trial artifacts)Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_5e2048a37e364924. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_5e2048a37e364924 · verifier authoritative; classifier explanatory.