{"id":50497606,"url":"https://github.com/stackql/stackql-audit-action","last_synced_at":"2026-06-02T09:02:49.093Z","repository":{"id":358859783,"uuid":"1243114032","full_name":"stackql/stackql-audit-action","owner":"stackql","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-31T08:28:17.000Z","size":306,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T10:12:28.779Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stackql.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-19T04:05:01.000Z","updated_at":"2026-05-31T08:28:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stackql/stackql-audit-action","commit_stats":null,"previous_names":["stackql/stackql-audit-action"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/stackql/stackql-audit-action","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackql%2Fstackql-audit-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackql%2Fstackql-audit-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackql%2Fstackql-audit-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackql%2Fstackql-audit-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stackql","download_url":"https://codeload.github.com/stackql/stackql-audit-action/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackql%2Fstackql-audit-action/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33814319,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-02T02:00:07.132Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-02T09:02:48.030Z","updated_at":"2026-06-02T09:02:49.084Z","avatar_url":"https://github.com/stackql.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# StackQL Cloud Audit Action\n\n\u003e **Live-audit GCP, AWS, and Azure with SQL — no agents, no pipelines, no ingestion.**\n\nA GitHub Action that runs an opinionated set of security checks against your\ncloud accounts using [stackql](https://stackql.io). Findings render as a\nmarkdown table on the workflow run page. Copy-paste setup, first results in\nunder two minutes.\n\n**A provider is audited only if its credentials are supplied** — start with one\ncloud, add the others later just by adding their secrets.\n\n\u003e Auditing a whole org (every project / region / subscription, not a single scope)? See **[Deep audits](/docs/deep-audit.md)**.\n\n![sample summary](/docs/img/google-screenshot-summary.png)\n\n## What it finds\n\n| Provider | Check | Severity |\n| --- | --- | --- |\n| GCP | SSH (22/tcp) open to the internet | HIGH |\n| GCP | RDP (3389/tcp) open to the internet | HIGH |\n| GCP | Cloud SQL instances reachable on a public IP | HIGH |\n| GCP | Compute instances with public IPs | MEDIUM |\n| GCP | Compute instances using the default service account | MEDIUM |\n| GCP | Storage buckets without uniform bucket-level access | MEDIUM |\n| GCP | Default VPC network exists | MEDIUM |\n| AWS | SSH (22/tcp) open to the internet (security groups) | HIGH |\n| AWS | RDP (3389/tcp) open to the internet (security groups) | HIGH |\n| AWS | RDS instances publicly accessible | HIGH |\n| AWS | EC2 instances with a public IP | MEDIUM |\n| AWS | Default VPC exists | LOW |\n| Azure | SSH (22/tcp) open to the internet (NSG) | HIGH |\n| Azure | RDP (3389/tcp) open to the internet (NSG) | HIGH |\n| Azure | SQL servers with public network access | HIGH |\n| Azure | Storage accounts allowing public blob access | MEDIUM |\n\nEvery check is a single YAML file under [`queries/\u003cprovider\u003e/`](queries/) —\nfork, extend, or replace at will.\n\n## Quickstart\n\n1. Set the credentials for the cloud(s) you want to audit (see\n   [Credentials \u0026 configuration](#credentials--configuration)).\n2. Copy a ready-to-use workflow from **[`docs/examples/`](docs/examples/)** into\n   your repo's `.github/workflows/`:\n\n   | Example | Audits |\n   | --- | --- |\n   | [`all-clouds-audit-workflow-dispatch.yml`](docs/examples/all-clouds-audit-workflow-dispatch.yml) | GCP + AWS + Azure in one run |\n   | [`google-only-audit-workflow-dispatch.yml`](docs/examples/google-only-audit-workflow-dispatch.yml) | GCP, manual trigger |\n   | [`google-only-audit-pull-request.yml`](docs/examples/google-only-audit-pull-request.yml) | GCP, on every PR to `main` |\n   | [`aws-only-audit-workflow-dispatch.yml`](docs/examples/aws-only-audit-workflow-dispatch.yml) | AWS, manual trigger |\n   | [`azure-only-audit-workflow-dispatch.yml`](docs/examples/azure-only-audit-workflow-dispatch.yml) | Azure, manual trigger |\n\nOpen the workflow run → the audit summary renders inline on the run page.\n\n## Credentials \u0026 configuration\n\nStore **sensitive** values as repo **secrets** (Settings → Secrets and variables\n→ Actions → *Secrets*). Non-sensitive identifiers (project ID, region,\nsubscription ID) can be **variables**, or just typed in at run time.\n\n### GCP\n\n| Action input | Suggested GitHub home | Sensitive |\n| --- | --- | --- |\n| `gcp-credentials` | secret `GCP_SA_JSON` (service account JSON key) | **yes** |\n| `gcp-project-id` | variable / run-time input | no |\n\n### AWS\n\n| Action input | Suggested GitHub home | Sensitive |\n| --- | --- | --- |\n| `aws-access-key-id` | secret `AWS_ACCESS_KEY_ID` | **yes** |\n| `aws-secret-access-key` | secret `AWS_SECRET_ACCESS_KEY` | **yes** |\n| `aws-region` | variable / run-time input | no |\n\n### Azure\n\n| Action input | Suggested GitHub home | Sensitive |\n| --- | --- | --- |\n| `azure-client-secret` | secret `AZURE_CLIENT_SECRET` | **yes** |\n| `azure-tenant-id` | secret / variable `AZURE_TENANT_ID` | identifier |\n| `azure-client-id` | secret / variable `AZURE_CLIENT_ID` | identifier |\n| `azure-subscription-id` | variable / run-time input | no |\n\nAzure auth uses a service principal (the tenant/client/secret triplet) via\nstackql's `azure_default`.\n\n## Inputs\n\n| Name | Required | Default | Description |\n| --- | --- | --- | --- |\n| `gcp-project-id` | for GCP | — | GCP project ID; substituted as `${PROJECT_ID}` in google checks. |\n| `gcp-credentials` | for GCP | — | Full contents of a GCP service account JSON key. |\n| `google-provider-version` | no | pinned | stackql Google provider version (blank = latest). |\n| `aws-access-key-id` | for AWS | — | AWS access key ID. |\n| `aws-secret-access-key` | for AWS | — | AWS secret access key. |\n| `aws-region` | for AWS | — | AWS region; substituted as `${AWS_REGION}` in aws checks. |\n| `aws-provider-version` | no | latest | stackql AWS provider version. |\n| `azure-subscription-id` | for Azure | — | Azure subscription ID; substituted as `${SUBSCRIPTION_ID}` in azure checks. |\n| `azure-tenant-id` / `azure-client-id` / `azure-client-secret` | for Azure | — | Service principal credentials. |\n| `azure-provider-version` | no | latest | stackql Azure provider version. |\n| `queries-path` | no | *(built-in)* | Custom queries dir (must contain per-provider subdirs `google/` `aws/` `azure/`). |\n| `fail-on-severity` | no | `HIGH` | Fail the workflow on findings at this severity or above. `NONE` never fails. |\n| `stackql-version` | no | `latest` | stackql release to install. |\n| `upload-logs` | no | `false` | Upload per-invocation stackql logs as the `stackql-audit-logs` artifact. |\n| `log-retention-days` | no | `0` | Retention (days) for that artifact. `0` = repo default; integer in `[0, 90]`. |\n\nA provider's checks run **iff** its credentials are present, so unused\nprovider inputs can be left unset.\n\n## Outputs\n\n| Name | Description |\n| --- | --- |\n| `findings-count` | Total findings across all checks. |\n| `highest-severity` | `CRITICAL` / `HIGH` / `MEDIUM` / `LOW` / `NONE`. |\n\n## Required permissions\n\nRead-only, on the audited scope:\n\n- **GCP** — `roles/compute.viewer`, `roles/cloudsql.viewer`,\n  `roles/storage.objectViewer`, `roles/iam.securityReviewer` (recommended).\n- **AWS** — the managed `SecurityAudit` policy (or `ReadOnlyAccess`) covers the\n  EC2/RDS describe calls the checks make.\n- **Azure** — the `Reader` role on the subscription.\n\nFor the **org-wide deep audits** (`scripts/discover.py`), grant the same read\nroles **one scope up** so they inherit, plus enumeration rights:\n\n- **GCP** — `roles/viewer` + `roles/resourcemanager.folderViewer` at the\n  **organization** node (Viewer covers the resource reads + project listing;\n  folderViewer adds folder descent).\n- **AWS** — `SecurityAudit` (or `ReadOnlyAccess`) on the principal; the S3 deep\n  scan additionally needs `s3:GetBucket*` and, if it routes via Cloud Control,\n  `cloudcontrol:GetResource`/`ListResources` (`ReadOnlyAccess` includes these).\n- **Azure** — `Reader` at the **management-group** (or tenant root) scope, which\n  inherits to every child subscription and covers the management-group descent.\n\n## Custom checks\n\nPoint `queries-path` at your own directory of per-provider subdirs. Each file\nis one check:\n\n```yaml\nid: my-org-firewall-check\nname: Allow only known source ranges\nseverity: HIGH\ndescription: Catch firewall rules with sourceRanges outside our corp CIDRs.\nremediation: Restrict to known CIDRs or migrate to IAP.\nquery: |\n  SELECT name, network, sourceRanges\n  FROM google.compute.firewalls\n  WHERE project = '${PROJECT_ID}'\n    AND direction = 'INGRESS'\ncolumns: [name, network, sourceRanges]\n```\n\nA query returning **zero rows = no findings**. Any rows returned become finding\nrows. For checks where SQL alone can't express the audit logic (e.g. filtering\nby structure inside a nested column), reference a function in\n[`scripts/filters.py`](scripts/filters.py):\n\n```yaml\nfilter: firewall_allows_port\nfilter_args:\n  port: 22\n  protocol: tcp\n```\n\n## Logs \u0026 debugging\n\nSet `upload-logs: true` to capture a per-invocation log for every `stackql exec`\n(the query, exit code, and stderr) and upload it as the `stackql-audit-logs`\nartifact. This is the fastest way to diagnose a check that returns nothing\nunexpectedly — stderr is recorded even when the query exits 0. Tune retention\nwith `log-retention-days` (`0` = repo default, max `90`).\n\n## How it works\n\n```\n   ┌───────────────────────────┐\n   │  queries/\u003cprovider\u003e/*.yaml │   (one file per check, per cloud)\n   └────────────┬──────────────┘\n                │\n                ▼\n   ┌───────────────────────────┐\n   │  audit.py                 │   fans out N parallel\n   │  └─ ThreadPoolExecutor    │   `stackql exec` subprocesses\n   └────────────┬──────────────┘\n                │\n                ▼\n   ┌────────────────────────────────────┐\n   │  stackql exec --output json         │\n   │   SELECT ... FROM google.*/aws.*/   │ \u003c─── live API calls\n   │                       azure.*       │\n   └────────────┬────────────────────────┘\n                │\n                ▼\n   ┌───────────────────────────┐\n   │  optional filter (Python) │   for checks SQL can't express\n   └────────────┬──────────────┘\n                │\n                ▼\n   ┌───────────────────────────┐\n   │  $GITHUB_STEP_SUMMARY     │   markdown tables, severity badges\n   └───────────────────────────┘\n```\n\nOne combined `--auth` object carries every supplied provider, so a single fan-out\ncovers all three clouds. Each check runs in its own short-lived stackql process —\ncleanup is automatic and checks run concurrently (8 at a time by default;\noverride with `STACKQL_AUDIT_PARALLEL`).\n\n## Why stackql\n\nstackql queries cloud control planes **live**. No resource crawl, no inventory\ndatabase, no daily sync — every `SELECT` hits the cloud API at query time. You\nsee what the cloud sees right now.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackql%2Fstackql-audit-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstackql%2Fstackql-audit-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackql%2Fstackql-audit-action/lists"}