From d8de7496317f1fe04a989915d9ef5c32297fbf95 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sat, 1 Aug 2026 06:54:52 +1000 Subject: [PATCH 1/4] updates --- query-library/queries/aws/ec2/instance-create.md | 2 +- query-library/queries/aws/iam/users-list.md | 5 ++--- query-library/queries/aws/lambda/functions-list.md | 3 +-- query-library/queries/aws/s3/bucket-detail.md | 2 +- query-library/queries/aws/s3/public-access-audit.md | 2 +- query-library/scripts/nightly-verify.py | 3 ++- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/query-library/queries/aws/ec2/instance-create.md b/query-library/queries/aws/ec2/instance-create.md index 57ce2bd7..3addc511 100644 --- a/query-library/queries/aws/ec2/instance-create.md +++ b/query-library/queries/aws/ec2/instance-create.md @@ -12,7 +12,7 @@ intent_keywords: - create a new vm in aws - provision an ec2 instance auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["cloudformation:CreateResource", "ec2:RunInstances", "ec2:CreateTags", "ec2:DescribeInstances"] +permissions: ["ec2:RunInstances", "ec2:CreateTags", "ec2:DescribeInstances"] params: - name: region type: identifier diff --git a/query-library/queries/aws/iam/users-list.md b/query-library/queries/aws/iam/users-list.md index a4226412..0553916c 100644 --- a/query-library/queries/aws/iam/users-list.md +++ b/query-library/queries/aws/iam/users-list.md @@ -12,7 +12,7 @@ intent_keywords: - what users exist in the account - iam user inventory auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["cloudformation:ListResources", "iam:ListUsers"] +permissions: ["iam:ListUsers"] params: [] outputs: - name: user_name @@ -55,8 +55,7 @@ WHERE region = 'us-east-1'; IAM is global: region = 'us-east-1' is endpoint routing, never a filter, and applies to every IAM query. The template is the cheap identifier-only path -via Cloud Control, requiring cloudformation:ListResources plus -iam:ListUsers; the native variation requires only iam:ListUsers and returns +via Cloud Control, requiring iam:ListUsers; the native variation requires only iam:ListUsers and returns ARN, user id, creation date, password_last_used and path in one call. password_last_used is the string null for users who have never signed in with a console password. Sibling list-only resources with the same shape diff --git a/query-library/queries/aws/lambda/functions-list.md b/query-library/queries/aws/lambda/functions-list.md index 08bc6e22..87dd18cf 100644 --- a/query-library/queries/aws/lambda/functions-list.md +++ b/query-library/queries/aws/lambda/functions-list.md @@ -113,5 +113,4 @@ so a projection valid for one form fails against the other. architectures is a JSON array - functions declare exactly one architecture today, so element 0 is the value. runtime is null for container image functions, where package_type is Image and the runtime lives in the image itself. memory_size -determines CPU allocation as well as memory. The awscc list-only variation -needs cloudformation:ListResources in addition to the Lambda action. +determines CPU allocation as well as memory. diff --git a/query-library/queries/aws/s3/bucket-detail.md b/query-library/queries/aws/s3/bucket-detail.md index 087f30ce..2ad4b89d 100644 --- a/query-library/queries/aws/s3/bucket-detail.md +++ b/query-library/queries/aws/s3/bucket-detail.md @@ -12,7 +12,7 @@ intent_keywords: - bucket security settings - s3 bucket detail auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetEncryptionConfiguration", "s3:GetBucketVersioning", "s3:GetBucketOwnershipControls", "s3:GetBucketLogging"] +permissions: ["s3:GetBucketPublicAccessBlock", "s3:GetEncryptionConfiguration", "s3:GetBucketVersioning", "s3:GetBucketOwnershipControls", "s3:GetBucketLogging"] params: - name: region type: identifier diff --git a/query-library/queries/aws/s3/public-access-audit.md b/query-library/queries/aws/s3/public-access-audit.md index 5b579633..43b61421 100644 --- a/query-library/queries/aws/s3/public-access-audit.md +++ b/query-library/queries/aws/s3/public-access-audit.md @@ -12,7 +12,7 @@ intent_keywords: - find buckets with public access block disabled - s3 buckets with public acls or policies auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetBucketOwnershipControls"] +permissions: ["s3:GetBucketPublicAccessBlock", "s3:GetBucketOwnershipControls"] params: - name: region type: identifier diff --git a/query-library/scripts/nightly-verify.py b/query-library/scripts/nightly-verify.py index 090ef54a..06ed4d5f 100644 --- a/query-library/scripts/nightly-verify.py +++ b/query-library/scripts/nightly-verify.py @@ -106,7 +106,8 @@ def main() -> int: args = parser.parse_args() try: - overrides = json.loads(args.params_json) + # An unset secret reaches us as an empty env var; treat blank as no overrides. + overrides = json.loads(args.params_json.strip() or "{}") except json.JSONDecodeError as e: print(f"nightly-verify: bad params JSON: {e}", file=sys.stderr) return 1 From 304307128dd78952f35872d159c34bbaa9b5df2e Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sat, 1 Aug 2026 06:59:38 +1000 Subject: [PATCH 2/4] regenerate artifacts for permission updates Co-Authored-By: Claude Fable 5 --- static/docs/query-library/index.json | 2 +- static/docs/query-library/index.md | 2 +- static/docs/query-library/manifest.json | 6 +++--- .../docs/query-library/queries/aws/ec2/instance-create.json | 1 - .../docs/query-library/queries/aws/ec2/instance-create.md | 2 +- static/docs/query-library/queries/aws/iam/users-list.json | 3 +-- static/docs/query-library/queries/aws/iam/users-list.md | 5 ++--- .../query-library/queries/aws/lambda/functions-list.json | 2 +- .../docs/query-library/queries/aws/lambda/functions-list.md | 3 +-- static/docs/query-library/queries/aws/s3/bucket-detail.json | 1 - static/docs/query-library/queries/aws/s3/bucket-detail.md | 2 +- .../query-library/queries/aws/s3/public-access-audit.json | 1 - .../query-library/queries/aws/s3/public-access-audit.md | 2 +- 13 files changed, 13 insertions(+), 19 deletions(-) diff --git a/static/docs/query-library/index.json b/static/docs/query-library/index.json index ece09ee4..aa003481 100644 --- a/static/docs/query-library/index.json +++ b/static/docs/query-library/index.json @@ -1,5 +1,5 @@ { - "build_id": "ql-ea4d2cbf5ec44568", + "build_id": "ql-c06517a27682cd09", "entries": [ { "id": "aws/cloud_control/resource-request-by-token", diff --git a/static/docs/query-library/index.md b/static/docs/query-library/index.md index 65ede0d2..8eb6d940 100644 --- a/static/docs/query-library/index.md +++ b/static/docs/query-library/index.md @@ -6,7 +6,7 @@ > (`.json`) consumed by the stackql MCP server's `query_library_search` > and `query_library_get` tools. -Build `ql-ea4d2cbf5ec44568` | 47 entries | machine catalogue: +Build `ql-c06517a27682cd09` | 47 entries | machine catalogue: [index.json](https://stackql.io/docs/query-library/index.json) | [manifest.json](https://stackql.io/docs/query-library/manifest.json) diff --git a/static/docs/query-library/manifest.json b/static/docs/query-library/manifest.json index 47ff79aa..7db118e4 100644 --- a/static/docs/query-library/manifest.json +++ b/static/docs/query-library/manifest.json @@ -1,6 +1,6 @@ { - "build_id": "ql-ea4d2cbf5ec44568", - "generated_at": "2026-07-31T03:10:30Z", - "library_commit": "3a2c3d9b", + "build_id": "ql-c06517a27682cd09", + "generated_at": "2026-07-31T20:59:23Z", + "library_commit": "d8de7496", "entry_count": 47 } diff --git a/static/docs/query-library/queries/aws/ec2/instance-create.json b/static/docs/query-library/queries/aws/ec2/instance-create.json index a47114d0..4fb41a92 100644 --- a/static/docs/query-library/queries/aws/ec2/instance-create.json +++ b/static/docs/query-library/queries/aws/ec2/instance-create.json @@ -16,7 +16,6 @@ "AWS_SECRET_ACCESS_KEY" ], "permissions": [ - "cloudformation:CreateResource", "ec2:RunInstances", "ec2:CreateTags", "ec2:DescribeInstances" diff --git a/static/docs/query-library/queries/aws/ec2/instance-create.md b/static/docs/query-library/queries/aws/ec2/instance-create.md index 57ce2bd7..3addc511 100644 --- a/static/docs/query-library/queries/aws/ec2/instance-create.md +++ b/static/docs/query-library/queries/aws/ec2/instance-create.md @@ -12,7 +12,7 @@ intent_keywords: - create a new vm in aws - provision an ec2 instance auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["cloudformation:CreateResource", "ec2:RunInstances", "ec2:CreateTags", "ec2:DescribeInstances"] +permissions: ["ec2:RunInstances", "ec2:CreateTags", "ec2:DescribeInstances"] params: - name: region type: identifier diff --git a/static/docs/query-library/queries/aws/iam/users-list.json b/static/docs/query-library/queries/aws/iam/users-list.json index 70ec4689..70cd9e85 100644 --- a/static/docs/query-library/queries/aws/iam/users-list.json +++ b/static/docs/query-library/queries/aws/iam/users-list.json @@ -16,7 +16,6 @@ "AWS_SECRET_ACCESS_KEY" ], "permissions": [ - "cloudformation:ListResources", "iam:ListUsers" ], "params": [], @@ -32,7 +31,7 @@ "expensive": false }, "template": "SELECT user_name\nFROM awscc.iam.users_list_only\nWHERE region = 'us-east-1';", - "notes": "IAM is global: region = 'us-east-1' is endpoint routing, never a filter, and applies to every IAM query. The template is the cheap identifier-only path via Cloud Control, requiring cloudformation:ListResources plus iam:ListUsers; the native variation requires only iam:ListUsers and returns ARN, user id, creation date, password_last_used and path in one call. password_last_used is the string null for users who have never signed in with a console password. Sibling list-only resources with the same shape cover roles (awscc.iam.roles_list_only) and groups (awscc.iam.groups_list_only).", + "notes": "IAM is global: region = 'us-east-1' is endpoint routing, never a filter, and applies to every IAM query. The template is the cheap identifier-only path via Cloud Control, requiring iam:ListUsers; the native variation requires only iam:ListUsers and returns ARN, user id, creation date, password_last_used and path in one call. password_last_used is the string null for users who have never signed in with a console password. Sibling list-only resources with the same shape cover roles (awscc.iam.roles_list_only) and groups (awscc.iam.groups_list_only).", "doc_url": "https://stackql.io/docs/query-library/queries/aws/iam/users-list", "last_verified": "2026-07-29", "author": "Jeffrey Aven", diff --git a/static/docs/query-library/queries/aws/iam/users-list.md b/static/docs/query-library/queries/aws/iam/users-list.md index a4226412..0553916c 100644 --- a/static/docs/query-library/queries/aws/iam/users-list.md +++ b/static/docs/query-library/queries/aws/iam/users-list.md @@ -12,7 +12,7 @@ intent_keywords: - what users exist in the account - iam user inventory auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["cloudformation:ListResources", "iam:ListUsers"] +permissions: ["iam:ListUsers"] params: [] outputs: - name: user_name @@ -55,8 +55,7 @@ WHERE region = 'us-east-1'; IAM is global: region = 'us-east-1' is endpoint routing, never a filter, and applies to every IAM query. The template is the cheap identifier-only path -via Cloud Control, requiring cloudformation:ListResources plus -iam:ListUsers; the native variation requires only iam:ListUsers and returns +via Cloud Control, requiring iam:ListUsers; the native variation requires only iam:ListUsers and returns ARN, user id, creation date, password_last_used and path in one call. password_last_used is the string null for users who have never signed in with a console password. Sibling list-only resources with the same shape diff --git a/static/docs/query-library/queries/aws/lambda/functions-list.json b/static/docs/query-library/queries/aws/lambda/functions-list.json index bfdbcbb7..ee75f686 100644 --- a/static/docs/query-library/queries/aws/lambda/functions-list.json +++ b/static/docs/query-library/queries/aws/lambda/functions-list.json @@ -85,7 +85,7 @@ "aws/ec2/regions-enabled" ], "template": "SELECT\nfunction_name,\nruntime,\njson_extract(architectures, '$[0]') as architecture,\nmemory_size,\ntimeout,\npackage_type,\ncode_size,\nlast_modified,\nrole\nFROM aws.lambda.functions\nWHERE region = '{{region}}';", - "notes": "The same resource serves two operations: region alone lists every function with full configuration, while adding function_name routes to GetFunction and returns a different column set (code, configuration, concurrency, tags), so a projection valid for one form fails against the other. architectures is a JSON array - functions declare exactly one architecture today, so element 0 is the value. runtime is null for container image functions, where package_type is Image and the runtime lives in the image itself. memory_size determines CPU allocation as well as memory. The awscc list-only variation needs cloudformation:ListResources in addition to the Lambda action.", + "notes": "The same resource serves two operations: region alone lists every function with full configuration, while adding function_name routes to GetFunction and returns a different column set (code, configuration, concurrency, tags), so a projection valid for one form fails against the other. architectures is a JSON array - functions declare exactly one architecture today, so element 0 is the value. runtime is null for container image functions, where package_type is Image and the runtime lives in the image itself. memory_size determines CPU allocation as well as memory.", "doc_url": "https://stackql.io/docs/query-library/queries/aws/lambda/functions-list", "last_verified": "2026-07-29" } diff --git a/static/docs/query-library/queries/aws/lambda/functions-list.md b/static/docs/query-library/queries/aws/lambda/functions-list.md index 08bc6e22..87dd18cf 100644 --- a/static/docs/query-library/queries/aws/lambda/functions-list.md +++ b/static/docs/query-library/queries/aws/lambda/functions-list.md @@ -113,5 +113,4 @@ so a projection valid for one form fails against the other. architectures is a JSON array - functions declare exactly one architecture today, so element 0 is the value. runtime is null for container image functions, where package_type is Image and the runtime lives in the image itself. memory_size -determines CPU allocation as well as memory. The awscc list-only variation -needs cloudformation:ListResources in addition to the Lambda action. +determines CPU allocation as well as memory. diff --git a/static/docs/query-library/queries/aws/s3/bucket-detail.json b/static/docs/query-library/queries/aws/s3/bucket-detail.json index 5e469403..ce316f9d 100644 --- a/static/docs/query-library/queries/aws/s3/bucket-detail.json +++ b/static/docs/query-library/queries/aws/s3/bucket-detail.json @@ -16,7 +16,6 @@ "AWS_SECRET_ACCESS_KEY" ], "permissions": [ - "cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetEncryptionConfiguration", "s3:GetBucketVersioning", diff --git a/static/docs/query-library/queries/aws/s3/bucket-detail.md b/static/docs/query-library/queries/aws/s3/bucket-detail.md index 087f30ce..2ad4b89d 100644 --- a/static/docs/query-library/queries/aws/s3/bucket-detail.md +++ b/static/docs/query-library/queries/aws/s3/bucket-detail.md @@ -12,7 +12,7 @@ intent_keywords: - bucket security settings - s3 bucket detail auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetEncryptionConfiguration", "s3:GetBucketVersioning", "s3:GetBucketOwnershipControls", "s3:GetBucketLogging"] +permissions: ["s3:GetBucketPublicAccessBlock", "s3:GetEncryptionConfiguration", "s3:GetBucketVersioning", "s3:GetBucketOwnershipControls", "s3:GetBucketLogging"] params: - name: region type: identifier diff --git a/static/docs/query-library/queries/aws/s3/public-access-audit.json b/static/docs/query-library/queries/aws/s3/public-access-audit.json index db264806..6cce3ca7 100644 --- a/static/docs/query-library/queries/aws/s3/public-access-audit.json +++ b/static/docs/query-library/queries/aws/s3/public-access-audit.json @@ -16,7 +16,6 @@ "AWS_SECRET_ACCESS_KEY" ], "permissions": [ - "cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetBucketOwnershipControls" ], diff --git a/static/docs/query-library/queries/aws/s3/public-access-audit.md b/static/docs/query-library/queries/aws/s3/public-access-audit.md index 5b579633..43b61421 100644 --- a/static/docs/query-library/queries/aws/s3/public-access-audit.md +++ b/static/docs/query-library/queries/aws/s3/public-access-audit.md @@ -12,7 +12,7 @@ intent_keywords: - find buckets with public access block disabled - s3 buckets with public acls or policies auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetBucketOwnershipControls"] +permissions: ["s3:GetBucketPublicAccessBlock", "s3:GetBucketOwnershipControls"] params: - name: region type: identifier From 0a49c5de5d116ec2b9d8c03e011dfda903940027 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sat, 1 Aug 2026 07:03:51 +1000 Subject: [PATCH 3/4] restore cloudformation:* permissions on awscc-backed entries Cloud Control API operations authorize under the cloudformation: IAM prefix (per the AWS Cloud Control API User Guide), so awscc-backed templates require these actions in addition to the downstream service actions. Co-Authored-By: Claude Fable 5 --- query-library/queries/aws/ec2/instance-create.md | 2 +- query-library/queries/aws/iam/users-list.md | 5 +++-- query-library/queries/aws/lambda/functions-list.md | 3 ++- query-library/queries/aws/s3/bucket-detail.md | 2 +- query-library/queries/aws/s3/public-access-audit.md | 2 +- static/docs/query-library/index.json | 2 +- static/docs/query-library/index.md | 2 +- static/docs/query-library/manifest.json | 6 +++--- .../docs/query-library/queries/aws/ec2/instance-create.json | 1 + .../docs/query-library/queries/aws/ec2/instance-create.md | 2 +- static/docs/query-library/queries/aws/iam/users-list.json | 3 ++- static/docs/query-library/queries/aws/iam/users-list.md | 5 +++-- .../query-library/queries/aws/lambda/functions-list.json | 2 +- .../docs/query-library/queries/aws/lambda/functions-list.md | 3 ++- static/docs/query-library/queries/aws/s3/bucket-detail.json | 1 + static/docs/query-library/queries/aws/s3/bucket-detail.md | 2 +- .../query-library/queries/aws/s3/public-access-audit.json | 1 + .../query-library/queries/aws/s3/public-access-audit.md | 2 +- 18 files changed, 27 insertions(+), 19 deletions(-) diff --git a/query-library/queries/aws/ec2/instance-create.md b/query-library/queries/aws/ec2/instance-create.md index 3addc511..57ce2bd7 100644 --- a/query-library/queries/aws/ec2/instance-create.md +++ b/query-library/queries/aws/ec2/instance-create.md @@ -12,7 +12,7 @@ intent_keywords: - create a new vm in aws - provision an ec2 instance auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["ec2:RunInstances", "ec2:CreateTags", "ec2:DescribeInstances"] +permissions: ["cloudformation:CreateResource", "ec2:RunInstances", "ec2:CreateTags", "ec2:DescribeInstances"] params: - name: region type: identifier diff --git a/query-library/queries/aws/iam/users-list.md b/query-library/queries/aws/iam/users-list.md index 0553916c..a4226412 100644 --- a/query-library/queries/aws/iam/users-list.md +++ b/query-library/queries/aws/iam/users-list.md @@ -12,7 +12,7 @@ intent_keywords: - what users exist in the account - iam user inventory auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["iam:ListUsers"] +permissions: ["cloudformation:ListResources", "iam:ListUsers"] params: [] outputs: - name: user_name @@ -55,7 +55,8 @@ WHERE region = 'us-east-1'; IAM is global: region = 'us-east-1' is endpoint routing, never a filter, and applies to every IAM query. The template is the cheap identifier-only path -via Cloud Control, requiring iam:ListUsers; the native variation requires only iam:ListUsers and returns +via Cloud Control, requiring cloudformation:ListResources plus +iam:ListUsers; the native variation requires only iam:ListUsers and returns ARN, user id, creation date, password_last_used and path in one call. password_last_used is the string null for users who have never signed in with a console password. Sibling list-only resources with the same shape diff --git a/query-library/queries/aws/lambda/functions-list.md b/query-library/queries/aws/lambda/functions-list.md index 87dd18cf..08bc6e22 100644 --- a/query-library/queries/aws/lambda/functions-list.md +++ b/query-library/queries/aws/lambda/functions-list.md @@ -113,4 +113,5 @@ so a projection valid for one form fails against the other. architectures is a JSON array - functions declare exactly one architecture today, so element 0 is the value. runtime is null for container image functions, where package_type is Image and the runtime lives in the image itself. memory_size -determines CPU allocation as well as memory. +determines CPU allocation as well as memory. The awscc list-only variation +needs cloudformation:ListResources in addition to the Lambda action. diff --git a/query-library/queries/aws/s3/bucket-detail.md b/query-library/queries/aws/s3/bucket-detail.md index 2ad4b89d..087f30ce 100644 --- a/query-library/queries/aws/s3/bucket-detail.md +++ b/query-library/queries/aws/s3/bucket-detail.md @@ -12,7 +12,7 @@ intent_keywords: - bucket security settings - s3 bucket detail auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["s3:GetBucketPublicAccessBlock", "s3:GetEncryptionConfiguration", "s3:GetBucketVersioning", "s3:GetBucketOwnershipControls", "s3:GetBucketLogging"] +permissions: ["cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetEncryptionConfiguration", "s3:GetBucketVersioning", "s3:GetBucketOwnershipControls", "s3:GetBucketLogging"] params: - name: region type: identifier diff --git a/query-library/queries/aws/s3/public-access-audit.md b/query-library/queries/aws/s3/public-access-audit.md index 43b61421..5b579633 100644 --- a/query-library/queries/aws/s3/public-access-audit.md +++ b/query-library/queries/aws/s3/public-access-audit.md @@ -12,7 +12,7 @@ intent_keywords: - find buckets with public access block disabled - s3 buckets with public acls or policies auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["s3:GetBucketPublicAccessBlock", "s3:GetBucketOwnershipControls"] +permissions: ["cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetBucketOwnershipControls"] params: - name: region type: identifier diff --git a/static/docs/query-library/index.json b/static/docs/query-library/index.json index aa003481..ece09ee4 100644 --- a/static/docs/query-library/index.json +++ b/static/docs/query-library/index.json @@ -1,5 +1,5 @@ { - "build_id": "ql-c06517a27682cd09", + "build_id": "ql-ea4d2cbf5ec44568", "entries": [ { "id": "aws/cloud_control/resource-request-by-token", diff --git a/static/docs/query-library/index.md b/static/docs/query-library/index.md index 8eb6d940..65ede0d2 100644 --- a/static/docs/query-library/index.md +++ b/static/docs/query-library/index.md @@ -6,7 +6,7 @@ > (`.json`) consumed by the stackql MCP server's `query_library_search` > and `query_library_get` tools. -Build `ql-c06517a27682cd09` | 47 entries | machine catalogue: +Build `ql-ea4d2cbf5ec44568` | 47 entries | machine catalogue: [index.json](https://stackql.io/docs/query-library/index.json) | [manifest.json](https://stackql.io/docs/query-library/manifest.json) diff --git a/static/docs/query-library/manifest.json b/static/docs/query-library/manifest.json index 7db118e4..233532d6 100644 --- a/static/docs/query-library/manifest.json +++ b/static/docs/query-library/manifest.json @@ -1,6 +1,6 @@ { - "build_id": "ql-c06517a27682cd09", - "generated_at": "2026-07-31T20:59:23Z", - "library_commit": "d8de7496", + "build_id": "ql-ea4d2cbf5ec44568", + "generated_at": "2026-07-31T21:03:45Z", + "library_commit": "30430712", "entry_count": 47 } diff --git a/static/docs/query-library/queries/aws/ec2/instance-create.json b/static/docs/query-library/queries/aws/ec2/instance-create.json index 4fb41a92..a47114d0 100644 --- a/static/docs/query-library/queries/aws/ec2/instance-create.json +++ b/static/docs/query-library/queries/aws/ec2/instance-create.json @@ -16,6 +16,7 @@ "AWS_SECRET_ACCESS_KEY" ], "permissions": [ + "cloudformation:CreateResource", "ec2:RunInstances", "ec2:CreateTags", "ec2:DescribeInstances" diff --git a/static/docs/query-library/queries/aws/ec2/instance-create.md b/static/docs/query-library/queries/aws/ec2/instance-create.md index 3addc511..57ce2bd7 100644 --- a/static/docs/query-library/queries/aws/ec2/instance-create.md +++ b/static/docs/query-library/queries/aws/ec2/instance-create.md @@ -12,7 +12,7 @@ intent_keywords: - create a new vm in aws - provision an ec2 instance auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["ec2:RunInstances", "ec2:CreateTags", "ec2:DescribeInstances"] +permissions: ["cloudformation:CreateResource", "ec2:RunInstances", "ec2:CreateTags", "ec2:DescribeInstances"] params: - name: region type: identifier diff --git a/static/docs/query-library/queries/aws/iam/users-list.json b/static/docs/query-library/queries/aws/iam/users-list.json index 70cd9e85..70ec4689 100644 --- a/static/docs/query-library/queries/aws/iam/users-list.json +++ b/static/docs/query-library/queries/aws/iam/users-list.json @@ -16,6 +16,7 @@ "AWS_SECRET_ACCESS_KEY" ], "permissions": [ + "cloudformation:ListResources", "iam:ListUsers" ], "params": [], @@ -31,7 +32,7 @@ "expensive": false }, "template": "SELECT user_name\nFROM awscc.iam.users_list_only\nWHERE region = 'us-east-1';", - "notes": "IAM is global: region = 'us-east-1' is endpoint routing, never a filter, and applies to every IAM query. The template is the cheap identifier-only path via Cloud Control, requiring iam:ListUsers; the native variation requires only iam:ListUsers and returns ARN, user id, creation date, password_last_used and path in one call. password_last_used is the string null for users who have never signed in with a console password. Sibling list-only resources with the same shape cover roles (awscc.iam.roles_list_only) and groups (awscc.iam.groups_list_only).", + "notes": "IAM is global: region = 'us-east-1' is endpoint routing, never a filter, and applies to every IAM query. The template is the cheap identifier-only path via Cloud Control, requiring cloudformation:ListResources plus iam:ListUsers; the native variation requires only iam:ListUsers and returns ARN, user id, creation date, password_last_used and path in one call. password_last_used is the string null for users who have never signed in with a console password. Sibling list-only resources with the same shape cover roles (awscc.iam.roles_list_only) and groups (awscc.iam.groups_list_only).", "doc_url": "https://stackql.io/docs/query-library/queries/aws/iam/users-list", "last_verified": "2026-07-29", "author": "Jeffrey Aven", diff --git a/static/docs/query-library/queries/aws/iam/users-list.md b/static/docs/query-library/queries/aws/iam/users-list.md index 0553916c..a4226412 100644 --- a/static/docs/query-library/queries/aws/iam/users-list.md +++ b/static/docs/query-library/queries/aws/iam/users-list.md @@ -12,7 +12,7 @@ intent_keywords: - what users exist in the account - iam user inventory auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["iam:ListUsers"] +permissions: ["cloudformation:ListResources", "iam:ListUsers"] params: [] outputs: - name: user_name @@ -55,7 +55,8 @@ WHERE region = 'us-east-1'; IAM is global: region = 'us-east-1' is endpoint routing, never a filter, and applies to every IAM query. The template is the cheap identifier-only path -via Cloud Control, requiring iam:ListUsers; the native variation requires only iam:ListUsers and returns +via Cloud Control, requiring cloudformation:ListResources plus +iam:ListUsers; the native variation requires only iam:ListUsers and returns ARN, user id, creation date, password_last_used and path in one call. password_last_used is the string null for users who have never signed in with a console password. Sibling list-only resources with the same shape diff --git a/static/docs/query-library/queries/aws/lambda/functions-list.json b/static/docs/query-library/queries/aws/lambda/functions-list.json index ee75f686..bfdbcbb7 100644 --- a/static/docs/query-library/queries/aws/lambda/functions-list.json +++ b/static/docs/query-library/queries/aws/lambda/functions-list.json @@ -85,7 +85,7 @@ "aws/ec2/regions-enabled" ], "template": "SELECT\nfunction_name,\nruntime,\njson_extract(architectures, '$[0]') as architecture,\nmemory_size,\ntimeout,\npackage_type,\ncode_size,\nlast_modified,\nrole\nFROM aws.lambda.functions\nWHERE region = '{{region}}';", - "notes": "The same resource serves two operations: region alone lists every function with full configuration, while adding function_name routes to GetFunction and returns a different column set (code, configuration, concurrency, tags), so a projection valid for one form fails against the other. architectures is a JSON array - functions declare exactly one architecture today, so element 0 is the value. runtime is null for container image functions, where package_type is Image and the runtime lives in the image itself. memory_size determines CPU allocation as well as memory.", + "notes": "The same resource serves two operations: region alone lists every function with full configuration, while adding function_name routes to GetFunction and returns a different column set (code, configuration, concurrency, tags), so a projection valid for one form fails against the other. architectures is a JSON array - functions declare exactly one architecture today, so element 0 is the value. runtime is null for container image functions, where package_type is Image and the runtime lives in the image itself. memory_size determines CPU allocation as well as memory. The awscc list-only variation needs cloudformation:ListResources in addition to the Lambda action.", "doc_url": "https://stackql.io/docs/query-library/queries/aws/lambda/functions-list", "last_verified": "2026-07-29" } diff --git a/static/docs/query-library/queries/aws/lambda/functions-list.md b/static/docs/query-library/queries/aws/lambda/functions-list.md index 87dd18cf..08bc6e22 100644 --- a/static/docs/query-library/queries/aws/lambda/functions-list.md +++ b/static/docs/query-library/queries/aws/lambda/functions-list.md @@ -113,4 +113,5 @@ so a projection valid for one form fails against the other. architectures is a JSON array - functions declare exactly one architecture today, so element 0 is the value. runtime is null for container image functions, where package_type is Image and the runtime lives in the image itself. memory_size -determines CPU allocation as well as memory. +determines CPU allocation as well as memory. The awscc list-only variation +needs cloudformation:ListResources in addition to the Lambda action. diff --git a/static/docs/query-library/queries/aws/s3/bucket-detail.json b/static/docs/query-library/queries/aws/s3/bucket-detail.json index ce316f9d..5e469403 100644 --- a/static/docs/query-library/queries/aws/s3/bucket-detail.json +++ b/static/docs/query-library/queries/aws/s3/bucket-detail.json @@ -16,6 +16,7 @@ "AWS_SECRET_ACCESS_KEY" ], "permissions": [ + "cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetEncryptionConfiguration", "s3:GetBucketVersioning", diff --git a/static/docs/query-library/queries/aws/s3/bucket-detail.md b/static/docs/query-library/queries/aws/s3/bucket-detail.md index 2ad4b89d..087f30ce 100644 --- a/static/docs/query-library/queries/aws/s3/bucket-detail.md +++ b/static/docs/query-library/queries/aws/s3/bucket-detail.md @@ -12,7 +12,7 @@ intent_keywords: - bucket security settings - s3 bucket detail auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["s3:GetBucketPublicAccessBlock", "s3:GetEncryptionConfiguration", "s3:GetBucketVersioning", "s3:GetBucketOwnershipControls", "s3:GetBucketLogging"] +permissions: ["cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetEncryptionConfiguration", "s3:GetBucketVersioning", "s3:GetBucketOwnershipControls", "s3:GetBucketLogging"] params: - name: region type: identifier diff --git a/static/docs/query-library/queries/aws/s3/public-access-audit.json b/static/docs/query-library/queries/aws/s3/public-access-audit.json index 6cce3ca7..db264806 100644 --- a/static/docs/query-library/queries/aws/s3/public-access-audit.json +++ b/static/docs/query-library/queries/aws/s3/public-access-audit.json @@ -16,6 +16,7 @@ "AWS_SECRET_ACCESS_KEY" ], "permissions": [ + "cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetBucketOwnershipControls" ], diff --git a/static/docs/query-library/queries/aws/s3/public-access-audit.md b/static/docs/query-library/queries/aws/s3/public-access-audit.md index 43b61421..5b579633 100644 --- a/static/docs/query-library/queries/aws/s3/public-access-audit.md +++ b/static/docs/query-library/queries/aws/s3/public-access-audit.md @@ -12,7 +12,7 @@ intent_keywords: - find buckets with public access block disabled - s3 buckets with public acls or policies auth: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY] -permissions: ["s3:GetBucketPublicAccessBlock", "s3:GetBucketOwnershipControls"] +permissions: ["cloudformation:GetResource", "s3:GetBucketPublicAccessBlock", "s3:GetBucketOwnershipControls"] params: - name: region type: identifier From 8d8958763ab3517f9ac0275df93cc4650504d095 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sat, 1 Aug 2026 07:11:18 +1000 Subject: [PATCH 4/4] add pre-commit hooks for query library validation and artifact freshness Two local hooks (validate, rebuild + freshness check) mirroring the CI gates, scoped to commits touching query-library/ or the committed artifacts. Per-clone opt-in: pip install pre-commit && pre-commit install. Co-Authored-By: Claude Fable 5 --- .pre-commit-config.yaml | 29 +++++++++ CLAUDE.md | 4 ++ CONTRIBUTING.md | 17 ++++++ query-library/scripts/precommit-artifacts.py | 64 ++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 .pre-commit-config.yaml create mode 100644 query-library/scripts/precommit-artifacts.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..168e627f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +# Pre-commit hooks for the query library pipeline. +# +# One-time setup per clone: +# pip install pre-commit +# pre-commit install +# +# Hooks run only when staged files touch the library sources or the +# committed artifacts. Both use language: python with the pipeline's +# dependencies (keep additional_dependencies in sync with +# query-library/scripts/requirements.txt), so no local pip install of the +# requirements is needed. CI enforces the same checks either way; the hooks +# just fail faster. +repos: + - repo: local + hooks: + - id: query-library-validate + name: query library structural validation + entry: python query-library/scripts/validate.py + language: python + additional_dependencies: ["PyYAML>=6.0", "jsonschema>=4.17"] + files: ^query-library/ + pass_filenames: false + - id: query-library-artifacts + name: query library artifacts freshness + entry: python query-library/scripts/precommit-artifacts.py + language: python + additional_dependencies: ["PyYAML>=6.0", "jsonschema>=4.17"] + files: ^(query-library/|static/docs/query-library/) + pass_filenames: false diff --git a/CLAUDE.md b/CLAUDE.md index 910a60e7..a2c624a3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -222,6 +222,10 @@ silently diverge from the committed artifact. python query-library/scripts/validate.py python query-library/scripts/build-artifacts.py +# Optional commit-time enforcement of the two commands above +# (.pre-commit-config.yaml; per-clone opt-in) +pip install pre-commit && pre-commit install + # Site (no env vars needed) yarn start # dev server - no postBuild AEO outputs, no JSON-LD yarn build # full production build diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5e17cdd0..bfddbe75 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -105,6 +105,23 @@ python query-library/scripts/build-artifacts.py Commit the regenerated files under `static/docs/query-library/` together with your entry. CI fails the PR if the committed artifacts do not match the sources. +### Pre-commit hooks (optional but recommended) + +The repo ships a `.pre-commit-config.yaml` that runs the same validation and +artifact-freshness checks at commit time, so you find out in seconds instead +of at CI. One-time setup per clone: + +```bash +pip install pre-commit +pre-commit install +``` + +The hooks only fire when a commit touches `query-library/` or the committed +artifacts. On a freshness failure the hook has already regenerated the +artifacts in your working tree - review them, `git add` them, and commit +again. The hooks install their own Python dependencies, and CI enforces the +same checks regardless, so skipping this setup just means slower feedback. + ## What CI checks Per PR: diff --git a/query-library/scripts/precommit-artifacts.py b/query-library/scripts/precommit-artifacts.py new file mode 100644 index 00000000..2f45a604 --- /dev/null +++ b/query-library/scripts/precommit-artifacts.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +"""Pre-commit gate: regenerate the committed artifacts and fail if the result +does not match what is staged. + +Runs build-artifacts.py (which validates entries first), then checks git +status for the generated outputs: static/docs/query-library/ and the +per-family .mdx stubs under query-library/. Any diff means the staged +sources and the staged artifacts are out of sync - the same condition the +CI freshness gate fails on. The rebuild is idempotent (build_id is a +content hash; manifest timestamps only change when it does), so a clean +tree passes with no side effects. + +pre-commit stashes unstaged changes before running hooks, so a diff here +always means "regenerated artifacts were not staged", never "you have +unrelated local edits". +""" + +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[2] +GENERATED_PATHSPECS = ["static/docs/query-library", "query-library/*.mdx"] +HANDWRITTEN = {"query-library/index.mdx"} + + +def main() -> int: + build = REPO_ROOT / "query-library" / "scripts" / "build-artifacts.py" + if subprocess.run([sys.executable, str(build)], cwd=REPO_ROOT).returncode != 0: + return 1 + + status = subprocess.run( + ["git", "status", "--porcelain", "--", *GENERATED_PATHSPECS], + cwd=REPO_ROOT, + capture_output=True, + text=True, + ) + if status.returncode != 0: + print(status.stderr, file=sys.stderr) + return 1 + + stale = [ + line + for line in status.stdout.splitlines() + if line.strip() and line[3:].strip('"') not in HANDWRITTEN + ] + if stale: + print("Regenerated artifacts differ from what is staged:", file=sys.stderr) + for line in stale: + print(f" {line}", file=sys.stderr) + print( + "\nThe build has refreshed these files in your working tree.\n" + "Review them, then: git add static/docs/query-library query-library/*.mdx\n" + "and commit again.", + file=sys.stderr, + ) + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main())