Hardcoded Credentials
Detects hardcoded passwords, API keys, tokens, and other sensitive credentials
87 rules in hardcoded_credentials.yml
CRITICAL: 44 | HIGH: 37 | MEDIUM: 5 | LOW: 1
| Rule ID | Severity | Title | Description | Refs |
|---|---|---|---|---|
anthropic_ | CRITICAL | Anthropic/Claude API Key | Matches an Anthropic / Claude API key (sk-ant-apiNN- + 95 chars). Keys grant billed model access on the owner’s workspace and must be rotated via console.anthropic.com. | |
aws_ | CRITICAL | AWS Access Key | Matches an AWS Access Key ID: long-term IAM user keys (AKIA + 16 base32) or temporary STS session credentials (ASIA + 16 base32). Both are the public half of a credential pair and routinely co-located with a leaked secret access key in the same file. Resource IDs (AROA / AIDA / ANPA / AGPA) are intentionally excluded - they are not secrets, only ARN components, and including them produces false positives on legitimate IAM role/user/policy references. | |
aws_ | CRITICAL | AWS MWS Auth Token | Matches an AWS Marketplace Web Service auth token (amzn.mws. | |
aws_ | CRITICAL | AWS Secret Access Key | Matches the 40-char AWS Secret Access Key shape associated with AKIA-prefixed keys. The secret half grants signed-request access to every service the IAM principal can reach. | |
aws_ | CRITICAL | AWS Temporary Session Token (ASIA…) Literal With Session Token Co-located | A task embeds an AWS session access-key-id prefixed ASIA (the STS-temporary-credentials prefix) together with an aws_session_token: / AWS_SESSION_TOKEN= literal. Unlike long-lived AKIA... keys which are almost always hard-coded by mistake, an ASIA... session token in a playbook is a strong indicator that a short-lived STS session was actively captured (credential-theft malware / IMDS-SSRF harvest / compromised MFA assume-role) and is being replayed from automation - the legitimate code path for ASIA credentials is boto3.Session().get_credentials() or aws sts assume-role, never a YAML literal. The token is valid for up to 36 hours by default, so rapid revocation is critical. | |
azure_ | CRITICAL | Azure Storage Key | Matches an Azure Storage account key (AccountKey= + 88 base64 chars). Account keys grant full-control access to the storage account; prefer SAS tokens or managed identities. | |
ci_ | CRITICAL | CI Environment Exfiltration (printenv / env -> curl / nc / paste) | A playbook or CI script dumps the process environment to a file or network destination: printenv | curl, env | base64 | curl, env > /tmp/leak && curl --upload-file, jq -n env | nc, or echo "$GITHUB_TOKEN" >> $GITHUB_STEP_SUMMARY. This is the canonical technique for sweeping every CI secret after compromising any build step - it exfiltrates GITHUB_TOKEN, AWS_*, PYPI_API_TOKEN, and everything else injected via the runner. | |
credit_ | CRITICAL | Credit-Card PAN (Visa/MC/Amex/Discover) Inline Literal In Playbook | A task contains a 13-19 digit string that matches the Luhn-valid PAN pattern for a major card brand AND passes a heuristic Luhn check - Visa (4[0-9]{12,18}), Mastercard (5[1-5][0-9]{14} or 2[2-7][0-9]{14}), Amex (3[47][0-9]{13}), Discover (6(?:011|5[0-9]{2})[0-9]{12}), JCB (35(?:2[89]|[3-8][0-9])[0-9]{12}), Diners (3(?:0[0-5]|[68][0-9])[0-9]{11}). A plaintext PAN in a playbook is a PCI-DSS Req-3.5 hard violation - the file, the Git history, the Ansible-callback log, the SIEM that ingested the callback, and every CI artifact that stored the playbook now hold cardholder data in scope. This rule deliberately tolerates test-PAN literals (4111 1111 1111 1111, 5555 5555 5555 4444, 4242 4242 4242 4242) because they still indicate cardholder-data-handling code that belongs in a PCI-DSS-scoped environment, not an Ansible playbook. | |
databricks_ | CRITICAL | Databricks Personal Access Token (dapi) Literal | A task embeds a Databricks PAT with the canonical dapi prefix followed by 32+ hex chars. Databricks PATs carry workspace-level or account-level API scope and are the #1 pivot used by attackers to exfiltrate Lakehouse data, run arbitrary notebooks, or abuse cluster compute for cryptomining. Databricks secret-scanning auto-revokes on push to GitHub/GitLab but any token in a playbook has typically already been committed. | |
entra_ | CRITICAL | Microsoft Entra (Azure AD) App Client Secret As Literal | A playbook sets AZURE_CLIENT_SECRET, client_secret, or azure_ad_client_secret to a literal that matches the Entra secret shape (<idx>~<40+ base64url chars> or a 32+ char opaque string passed to azure.azcollection modules). Entra app secrets permit minting tokens for every resource the app is consented to - including Application.ReadWrite.All / Directory.ReadWrite.All, which is full tenant takeover. | |
facebook_ | CRITICAL | Facebook Access Token | Matches a Facebook Graph API access token (EAA + 90+ chars). The token authorizes API calls scoped to the granting user/app and remains valid until expiry or revocation. | |
gcp_ | CRITICAL | GCP Service-Account JSON Private Key Inline In Playbook | A task embeds a GCP service-account key JSON inline ("type": "service_account" + -----BEGIN PRIVATE KEY-----). SA keys are long-lived (10-year default), carry project-level IAM permissions, and are the #1 GCP compromise vector - the 2024 Sysdig-reported AMBERSQUID campaign pivoted exclusively through leaked SA keys to mine crypto on GCP Cloud Run. Google is deprecating SA keys in favor of workload-identity federation (WIF) in 2025. | |
github_ | CRITICAL | GitHub Personal-Access-Token Or App-Token Literal In Playbook | A task embeds a GitHub token with one of the canonical prefixes - classic PAT (ghp_<36-base62>), OAuth user token (gho_), user-to-server token (ghu_), server-to-server token (ghs_), refresh token (ghr_), or fine-grained PAT (github_pat_<22-base62>_<59-base62>, 2022 format). These tokens bypass 2FA, typically carry repo, workflow, admin:org, or packages scopes, and are the #1 pivot used in 2024 supply-chain attacks (Toyota, CircleCI, Pulse Secure, Mercedes source leak) to push malicious commits, poison CI, or exfiltrate source. Detection is trivial (unique prefixes published by GitHub in their token-format spec), revocation SLA is seconds via gh auth revoke, and there is no legitimate reason for any of them to appear inline in configuration management. | |
github_ | CRITICAL | GitHub Personal Access Token | Matches a GitHub Personal Access Token (gh[pousr]_ + 36+ chars). Tokens grant repo, workflow, or org-admin scope depending on the prefix and need to be rotated via Settings -> Developer settings. | |
gitlab_ | CRITICAL | GitLab CI Job Token or Registry Password Leaked to External Host | A playbook / script echoes, curls, or posts $CI_JOB_TOKEN, $CI_REGISTRY_PASSWORD, $CI_DEPLOY_PASSWORD, CI_JOB_JWT, or CI_REGISTRY_TOKEN to a non-GitLab destination (webhook, pastebin, external log collector). CI_JOB_TOKEN authenticates as the pipeline and can push packages, read project secrets via the job-token API, and (with GitLab 16.2+) trigger downstream pipelines - leaking it outside GitLab is full pipeline-identity compromise. | |
google_ | CRITICAL | Google API Key | Matches the Google API key shape (AIza + 35 chars). The key grants access to whichever Google APIs are enabled on the owner’s project. | |
gpp_ | CRITICAL | Group Policy Preferences (Groups.xml) cpassword Literal - Trivially Decryptable | A task renders a Windows Group Policy Preferences XML file (Groups.xml, Services.xml, ScheduledTasks.xml, DataSources.xml, or Printers.xml under SYSVOL) containing a cpassword= attribute. Microsoft’s GPP feature stored local-admin / service-account passwords as AES-CBC-encrypted blobs using a public, published 32-byte key that Microsoft literally documented on MSDN - making any cpassword value trivially decryptable in <1 second with gpp-decrypt or a 10-line PowerShell snippet. Microsoft’s 2014 MS14-025 patch blocked NEW GPP creation but did NOT remove or re-encrypt existing entries - meaning SYSVOL on most pre-2014-provisioned domains STILL contains valid cpassword blobs decades later. An attacker with Authenticated-User read to SYSVOL (every domain user, by default) harvests every cpassword across the forest in one Get-GPPPassword call. This is the #1 ‘initial-access -> domain-admin’ technique in every AD pentest report since 2012 and still hits in 2024-2025 Mandiant M-Trends. | |
hardcoded_ | CRITICAL | Hardcoded API Key | An api_key: / apikey: value is set to a 10+ char literal instead of being looked up from vault, env, or a secret manager. Once committed, the key has to be rotated to be made safe. | |
hardcoded_ | CRITICAL | Hardcoded Password | Password field contains hardcoded value instead of using secure storage | |
hardcoded_ | CRITICAL | Hardcoded Secret | A secret: value is set to a 6+ char literal at the start of a line, not a Jinja2 expression. The literal lives in git history indefinitely; rotate the value as well as the file. | |
hardcoded_ | CRITICAL | Hardcoded Token | A token: value is set to a 10+ char literal instead of resolved from vault/env/secret manager. Tokens leaked into git remain valid until rotated upstream. | |
hashicorp_ | CRITICAL | HashiCorp Vault Root / Service Token Written As Literal | A playbook sets VAULT_TOKEN to a literal string beginning with hvs., hvb., s., or b. (the Vault token prefixes), or writes ~/.vault-token, or hard-codes a vault_token: in community.hashi_vault.* module calls. A Vault token is a bearer credential to every secret the policy grants - leaking one in an Ansible repository is effectively equivalent to leaking the entire secret namespace. | |
ipmi_ | CRITICAL | IPMI Credentials in Playbook | Contains IPMI/BMC credentials (username/password) in playbook | |
npm_ | CRITICAL | npm Automation/Publish Token (npm_) Literal | A task embeds an npm token with the canonical npm_ prefix (36 base62 chars). npm tokens with publish scope are THE supply-chain compromise vector - leaking one lets an attacker push malicious versions to every package the owner maintains (see ua-parser-js, coa, rc 2021-2024 incidents). npm’s secret-scanning auto-revokes on push to GitHub but anything reaching a playbook has already been committed. | |
npm_ | CRITICAL | NPM or PyPI Publish Token in Plaintext | A publish-scope registry token literal: npm automation/publish token (npm_[A-Za-z0-9]{36}), PyPI API token (pypi-AgE[A-Za-z0-9_-]{60,}), Twine password variable assignment (TWINE_PASSWORD=...), or a bare .npmrc / .pypirc line with a token value. These tokens can publish malicious package versions under the owning project’s name - arguably the highest-impact supply-chain credential. | |
oci_ | CRITICAL | OCI API Signing Key in Playbook | Contains OCI API key fingerprint or tenancy OCID that could be used for unauthorized access | |
okta_ | CRITICAL | Okta Admin API Token As Literal | A playbook sets OKTA_API_TOKEN, okta_token, or okta.oie.token to a literal matching Okta’s API-token format (00... or 0oa... 40+ base64url chars). An Okta admin token can create users, reset MFA factors, and read group membership for the entire tenant - it is the SSO equivalent of a root credential and is what the 2023 Okta Support breach leaked. | |
okta_ | CRITICAL | Okta API token (SSWS) hardcoded in playbook | An Okta API token is hardcoded as Authorization: SSWS 00<40+chars> or as a okta_api_token / OKTA_API_TOKEN variable literal. Okta API tokens carry the permissions of the creating admin (by default, super-admin) and grant unrestricted access to every user, app, group, and factor in the org - the canonical blast-radius one-token leak. Incidents in 2023-2024 (Okta HAR-file leak, Lapsus$ campaigns) were amplified by tokens stored in tooling repos. | |
openai_ | CRITICAL | OpenAI API Key | Matches an OpenAI API key (sk- + 48 chars). The key grants billed access to OpenAI’s models and must be rotated via the platform.openai.com dashboard once exposed. | |
paypal_ | CRITICAL | PayPal Braintree Token | Matches a PayPal Braintree access token (access_token$ | |
pypi_ | CRITICAL | PyPI API Token (pypi-) Literal | A task embeds a PyPI API token with the pypi- prefix (PEP 458 format). PyPI tokens with upload scope can push arbitrary wheels/sdists to any package the owner maintains - the vector used in the 2022 ctx/phpass compromise and the 2024 requests-darwin-lite typo-squat campaign. PyPI auto-revokes on GitHub push but committed tokens elsewhere (GitLab internal, file shares) are not caught. | |
rails_ | CRITICAL | Rails secret_key_base / master.key / credentials.yml.enc Key Inline Literal | A task renders a Rails application config (config/secrets.yml, config/credentials.yml.enc, config/master.key, .env, systemd Environment=SECRET_KEY_BASE=) with a literal 64-to-128-character hex/base64 value for secret_key_base, RAILS_MASTER_KEY, secret_token, or encrypted_secret. Rails’ secret_key_base signs session cookies and ActiveSupport::MessageEncryptor data. With the key, an attacker (1) forges ANY user’s session cookie (account takeover on every user), (2) crafts a poisoned Marshal-dumped cookie - historically RCE via CVE-2019-5420 / CVE-2020-8165-style deserialization gadgets, (3) decrypts credentials.yml.enc which commonly holds DB passwords + S3 keys + SMTP creds + Stripe keys + Google OAuth secrets. One leaked master.key = pwned application + pwned downstream services. 2023 GitHub Dependabot data showed master.key as the #3 most-committed-by-accident secret. | |
rancher_ | CRITICAL | Rancher v2 bootstrapPassword Set To admin Or Default-Weak Literal | A task sets bootstrapPassword: admin (or password, rancher, changeme) in a Rancher Helm values file / Ansible vars or exports CATTLE_BOOTSTRAP_PASSWORD=admin in an EE/Dockerfile. Rancher’s initial admin login with the bootstrap password is a full cluster-owner credential - Rancher proxies commands to every managed downstream Kubernetes cluster. The 2024 Unit42 report on cloud-native incident response lists Rancher-default-admin as a Top-10 kubernetes-takeover chain. | |
shopify_ | CRITICAL | Shopify API Access Token (shpat/shppa/shpca/shpss) Literal | A task embeds a Shopify token with one of the four canonical prefixes: shpat_ (Admin API access token, custom apps), shppa_ (Partner app token), shpca_ (Collaborator token), shpss_ (shared secret). Leaked Shopify tokens with write_orders / read_customers scope are the direct primitive behind 2024 e-commerce card-skimmer campaigns (Magecart-on-Shopify) that exfiltrate payment data via the Admin API. | |
snowflake_ | CRITICAL | Snowflake Password-Auth Inline Literal | A task passes password: <literal> to the community.general.snowflake_*, snowflake.sqlalchemy, or snowsql CLI (–password / -p). Following the 2024 UNC5537 campaign that mass-stole ~165 Snowflake customer accounts (Ticketmaster, Santander, AT&T, Advance Auto Parts) via credential-stuffed Snowflake passwords without MFA, any Snowflake password literal in config is high-risk. Snowflake deprecated password-only auth in Oct 2024 and will disable it entirely by Nov 2025. | |
sops_ | CRITICAL | SOPS age/GPG Private Key Written To Disk By Playbook | A task copies, templates, or assembles a SOPS age private key (begins AGE-SECRET-KEY-) or a SOPS GPG armored private key (-----BEGIN PGP PRIVATE KEY BLOCK-----) onto a target host, or sets SOPS_AGE_KEY / SOPS_AGE_KEY_FILE to a literal value. Whoever owns that key decrypts every file the organisation has ever encrypted with it - including every secret committed to Git. | |
square_ | CRITICAL | Square OAuth Secret | Matches a Square OAuth secret (sq0csp- + 43 chars). Once leaked, the secret allows minting OAuth tokens for any merchant who authorized the application. | |
stripe_ | CRITICAL | Stripe API Key | Matches a Stripe live or test API key (sk_live_ / sk_test_ + 24+ chars). Live keys can charge cards and refund money; both must be rotated through the Stripe dashboard once exposed. | |
stripe_ | CRITICAL | Stripe Live Secret Key (sk_live_ / rk_live_) Embedded In Playbook | A task embeds a Stripe live-mode secret key (sk_live_[A-Za-z0-9]{24,}) or restricted key (rk_live_[A-Za-z0-9]{24,}). A Stripe live secret key can issue charges, refund arbitrary charges to attacker bank accounts, read every saved card-on-file (PCI-DSS scope expansion), list all customers with PII, and create instant-payouts - there is effectively no blast-radius containment. Stripe’s breach-response runbook specifically treats a leaked sk_live_ as requiring rotation + customer notification under PCI-DSS 12.10.1. | |
twitter_ | CRITICAL | Twitter API Key | Matches a Twitter / X API key (numeric-id + 40-char alnum). Once leaked, the key authorizes API calls on the owner’s developer account until rotated. | |
url_ | CRITICAL | URL-Encoded Credentials | Form-encoded <name>=<value> where the name contains words like token/secret/auth/password/oauth/jwt. The shape catches credentials baked into query strings, callback URLs, and config snippets. | |
vault_ | CRITICAL | HashiCorp Vault Audit Device Disabled From Playbook (sys/audit Delete) | A task runs vault audit disable <path> or hits /v1/sys/audit/<path> with an HTTP DELETE. Disabling the only audit device on a running Vault cluster immediately turns the secrets-broker into a black-box - every subsequent read (vault kv get, token issuance, policy change) proceeds with zero forensic trail. This is the canonical post-compromise Vault move and is MITRE T1562.008 applied to secrets management; it is never an appropriate configuration-management primitive. | |
vault_ | CRITICAL | Vault Password File Committed To Repo | ansible.cfg (or CLI flag) references a vault_password_file that looks like it’s inside the repo tree (./vault_pass, ./.vault_pass) rather than a path outside version control. | |
vault_ | CRITICAL | HashiCorp Vault Root Token Or Unseal Keys Embedded Inline In Playbook | A task assigns a Vault root token (format hvs.*, s.*, or hvb.*) or Shamir unseal-key share inline - typically as vault_token: hvs.CAESIAbCd..., VAULT_TOKEN: s.1a2b3c..., unseal_keys: ['abc123...', 'def456...'], or passed via --unseal-key=. Root tokens bypass every Vault policy; unseal-key shares (if a single playbook holds 3-of-5 shares) reconstruct the master key and allow Vault to be unsealed anywhere the attacker wants. Both are one-line catastrophes that end up in Git, CI logs, and Ansible-callback JSON exports. | |
atlassian_ | HIGH | Atlassian / Jira API Token (ATATT) Literal | A task embeds an Atlassian API token with the canonical ATATT prefix (introduced 2023, ~192 chars). These tokens carry FULL user-equivalent access to Jira, Confluence, Bitbucket, Jira Service Management, and Opsgenie - including permission to read private source in Bitbucket, exfiltrate Confluence pages containing secrets/PII, and create Jira issues with attacker-controlled attachments that phish other employees. | |
cloudflare_ | HIGH | Cloudflare scoped API token hardcoded in playbook | A Cloudflare API token (Authorization: Bearer + 40-char base64url) or legacy Global API Key is hardcoded. Cloudflare tokens control DNS, WAF, SSL/TLS, Workers, R2, tunnels - a leaked token with Zone:Edit can pivot to full subdomain takeover and MITM via Workers. The legacy Global API Key grants full-account access and is even more dangerous. | |
cloudinary_ | HIGH | Cloudinary Credentials | Matches the cloudinary://<api_key>:<api_secret>@<cloud_name> URL scheme. The string carries both halves of the Cloudinary API credentials in one literal. | |
datadog_ | HIGH | Datadog API Key Or APP Key Embedded Inline In Playbook | A task assigns DD_API_KEY, DATADOG_API_KEY, DD_APP_KEY, or DATADOG_APP_KEY to a 32-character hex literal. A Datadog API key allows metric / log / trace ingestion into your account (garbage-in attacks skew SLO alerting), and more critically an APP key grants full API access: read every log line (often containing secrets that leaked into stdout), read every APM trace (HTTP request payloads), modify monitors to silence alerts, and invoke the Events API to inject spoofed security events. The 2023 CircleCI breach was prolonged because the attacker had a Datadog APP key and muted every alarm. | |
discord_ | HIGH | Discord Bot Token | Matches a Discord bot token (M/N/O + 23 chars . 6 chars . 27 chars). Bot tokens grant the bot’s full server permissions and rotate only via the Discord developer portal. | |
docker_ | HIGH | Docker Personal Access Token | Matches a Docker Hub personal access token (dckr_pat_ + 27 chars). Tokens authorize push/pull and image management on the owner’s Docker Hub account. | |
dockerhub_ | HIGH | Docker Hub Personal Access Token (dckr_pat_) Literal | A task embeds a Docker Hub PAT with the canonical dckr_pat_ prefix (30+ base64url chars). Docker Hub PATs replace the account password for docker login and CLI API calls and typically carry repo:write scope - the exact scope needed to push a malicious image to a shared org namespace. Used in the 2024 Snyk-reported supply-chain push where >1,600 malicious images appeared on Docker Hub under hijacked publisher accounts. | |
dynatrace_ | HIGH | Dynatrace Token | Matches a Dynatrace API token (dt0… + 24 + 64 chars). Dynatrace tokens grant scoped tenant API access and must be rotated via the Access tokens UI once exposed. | |
elastic_ | HIGH | Elastic APM secret_token Or api_key Inline Literal In Playbook / Jinja | A task renders an Elastic APM agent config (elastic-apm.yml, elasticapm.ini, env ELASTIC_APM_SECRET_TOKEN= / ELASTIC_APM_API_KEY=) with a literal, hex/base64-looking value of length ≥32 characters, not a Jinja {{ }} expression or Ansible Vault reference. APM secret tokens authenticate trace-data submission; an attacker with the token can POISON trace data (injecting fake spans to mask intrusion), EXFILTRATE legitimate traces (containing DB queries, request bodies, header values), or overflow the APM server with DoS. The long-lived nature of APM tokens + common log-collection into the same cluster make them a high-value credential secondary to the primary ES creds. | |
factory_ | HIGH | Factory-Default Credential Passed to curl/wget Basic Auth | A curl -u, curl --user, wget --user/--password, or http --auth invocation passes a known vendor factory-default credential as the password component. Vendor first-boot credentials are public knowledge (CIRT.net default-password database, NIST 800-53 baseline checks); reusing them in automation against a deployed appliance leaves the appliance authenticatable with a credential listed in every default-password database. Tokens covered include enterprise-appliance defaults (welcome, changeit, tigertiger, procurve, calvin, PASSW0RD, ADMIN, cisco, c1sco12345, tomcat, ubnt, mikrotik, pfsense, vagrant, raspberry, changeme) and the generic top-of-list passwords (password, 123456, 12345678, qwerty, letmein, toor, admin123, Admin@123, Passw0rd, P@ssw0rd, monitor, support, service). | |
grafana_ | HIGH | Grafana Service-Account Token (glsa_) Inline Literal (2023+ Long-Lived Token) | A task contains a Grafana service-account token with the glsa_ prefix (introduced in Grafana 9.1, Sep 2022, replacing the deprecated eyJrIjo... API-key format). Format: glsa_<40-base62>_<8-hex-checksum>. These tokens carry the full permission scope of a Grafana service-account (can create/edit/delete dashboards, alerts, datasources - datasources often contain downstream DB credentials that can be exfiltrated via Explore). Unlike the JWT-style API keys, glsa_ tokens do NOT expire by default and are specifically targeted by 2024 GrafanaCloud takeover reports. | |
heroku_ | HIGH | Heroku API Key | Matches the Heroku API key UUID format. The key controls all apps the owner can administer and must be rotated via heroku authorizations:create once exposed. | |
jwt_ | HIGH | JWT Token | Matches a JWT (header.payload.signature, base64url segments). JWTs often carry user identity and remain valid for the token’s lifetime; treat any committed JWT as compromised. | |
langsmith_ | HIGH | LangSmith API Key (lsv2_) Literal | A task embeds a LangSmith API key with the canonical lsv2_pt_ (personal token) or lsv2_sk_ (service key) prefix. LangSmith holds LLM traces with full prompt/response content - in most orgs these include PII, source code, API keys, and customer messages (all things developers accidentally include in LLM contexts). Leaked LangSmith keys are a high-value data-exfiltration primitive. | |
mailchimp_ | HIGH | MailChimp API Key | Matches a Mailchimp API key (32 hex + -us | |
mailgun_ | HIGH | Mailgun API Key | Matches a Mailgun API key (key- + 32 chars). The key authorizes sendmail-style API calls and must be rotated via the Mailgun control panel. | |
mailgun_ | HIGH | Mailgun API Key (key-) Literal | A task embeds a Mailgun API key with the canonical key- prefix (32 hex chars). Same BEC/phishing abuse pattern as SendGrid - Mailgun sender domains pass DMARC out-of-the-box when auth’d. | |
mfa_ | HIGH | MFA Disabled / Bypassed For Privileged Identity | A task sets mfa_enabled: false, require_mfa: false, AWS delete-virtual-mfa-device, Entra strongAuthenticationRequirements: [], or Okta factor: null for a user or role that is administrator / owner / global-admin. Disabling MFA for a privileged principal is one of the top-3 post-compromise persistence actions in both the 2024 Verizon DBIR and Microsoft’s 2025 Digital Defense Report. | |
mysql_ | HIGH | MySQL Password in Command | mysql command-line invocation passes -p’ | |
nomad_ | HIGH | HashiCorp Nomad Server gossip encrypt Key Empty Or Plaintext In Config | A Nomad server config (nomad.hcl, server { encrypt = "..." }, or community.general.nomad_job bootstrap) has an empty encrypt value, the literal string "", a value under 24 chars (not a valid base64 AES-256 key), or a hardcoded literal in the repo. Nomad gossip (Serf) is the inter-server membership protocol on 4648/tcp,udp. Without encryption, an attacker on the same L2 segment can join the Serf cluster, pretend to be a Nomad server, and poison job scheduling. 2024 HashiCorp hardening-guide mandates encrypt MUST be set AND supplied via a secrets manager. | |
plaintext_ | HIGH | Variable Named *_key Has Plaintext Credential-Shaped Value | A var whose name ends in _key is assigned a credential-shaped string literal: 16+ chars of base64 / hex / UUID / token charset, no path separators, no whitespace, not Jinja, not vault-encrypted. The narrow api_key / private_key rules above miss the long tail of vendor-specific *_key fields. Public-material keys (ssh_public_key, gpg_key, host_key, public_key, verify_key, verifying_key) and keys whose value is a filesystem path or URL are excluded. | |
plaintext_ | HIGH | Variable Named *password/*secret/*token Has Plaintext Value | A var with a name that screams ‘secret’ (db_password, api_token, jwt_secret, private_key, aws_secret_access_key, rootpw, svc_creds) is assigned a literal non-Jinja, non-vault, non-!vault-tagged value - the secret sits in plaintext in version control. Both quoted and unquoted YAML scalars are flagged. Path/file aliases (*_file, *_url) are intentionally excluded - they reference where a credential lives, not the credential itself. | |
portainer_ | HIGH | Portainer –admin-password / –admin-password-file Inline Default / Literal | A task launches Portainer (or portainer-ee) with --admin-password containing a bcrypt hash literal OR --admin-password-file pointing at a file with hardcoded content in the same repo. Portainer admin owns every Docker/Swarm/Kubernetes endpoint registered in it - a compromise here cascades to every connected cluster. The rule is tuned to hit the most common misconfig: --admin-password '$2y$05$...' embedded in a systemd unit or docker-compose. | |
postgresql_ | HIGH | PostgreSQL Password in Command | psql command-line invocation passes -p’ | |
sendgrid_ | HIGH | SendGrid API Key | Matches a SendGrid API key (SG. + 22 chars . 43 chars). SendGrid keys can send mail as any verified sender on the account; rotate via Settings -> API Keys. | |
sendgrid_ | HIGH | SendGrid API Key (SG.xxx.yyy) Literal | A task embeds a SendGrid API key with the canonical SG.<22-base64url>.<43-base64url> format. SendGrid keys with Mail Send scope are abused in business-email-compromise and phishing campaigns because the sender domain is pre-warmed and passes SPF/DKIM/DMARC from sendgrid.net. | |
slack_ | HIGH | Slack Bot/User/App Token Literal (xoxb / xoxp / xoxa / xoxs / xapp) In Playbook | A task embeds a Slack token with one of the Slack-published prefixes: xoxb- (bot), xoxp- (user), xoxa- (workspace app), xoxs- (legacy session), or xapp- (Socket-Mode app-level). A leaked Slack token grants attacker access to post messages as your bot (social-engineering / phishing-as-your-brand), read private channels (often containing rotated passwords and OAuth codes), list all users (corporate reconnaissance), and reset webhooks. Slack’s secret-scanner revokes on push to public GitHub, but internal git repos, ansible-log forwards, and callback-plugin JSON exports are not covered. | |
slack_ | HIGH | Slack Webhook URL | Matches a hooks.slack.com webhook URL with the team, channel, and 24-char secret embedded in the path. Anyone with the URL can post messages as the webhook integration. | |
splunk_ | HIGH | Splunk HEC (HTTP Event Collector) token hardcoded in playbook | A Splunk HEC token is hardcoded as Authorization: Splunk <uuid> or as HEC_TOKEN/splunk_hec_token literal. HEC tokens allow unauthenticated event submission on the configured index - a leaked token lets an attacker (1) pollute SIEM data with forged events to hide real intrusions, (2) consume license entitlement (DoS via ingest-quota exhaustion), (3) potentially trigger correlation-search actions that take operational effect. Observability-vendor tokens are under-scanned compared to cloud-provider keys. | |
sshpass_ | HIGH | SSHPass Password | sshpass -p ‘ | |
telegram_ | HIGH | Telegram Bot Token | Matches a Telegram bot token (numeric-id:35-char). Bot tokens authorize sendMessage, getUpdates, and channel administration; rotate via @BotFather once exposed. | |
terraform_ | HIGH | Terraform .tfvars File Contains Secret Literal | A playbook templates or writes a .tfvars / .auto.tfvars / terraform.tfvars.json file whose content contains a secret-looking assignment (password = "...", api_key = "...", aws_secret_access_key = "..."). Terraform writes the full variable value into terraform.tfstate, which is then typically committed to Git or stored in an unencrypted S3 bucket - making it discoverable by any historical-state grep. | |
twilio_ | HIGH | Twilio Account-SID (AC…) Plus Auth-Token Pair Embedded In Playbook | A task embeds a Twilio Account SID (AC[a-f0-9]{32}) co-located with a 32-hex AUTH_TOKEN / auth_token / TWILIO_AUTH_TOKEN value. The pair gives an attacker the ability to send SMS from your phone numbers (mass-phishing / MFA-bypass), read all SMS (MFA-code interception, the 2022 Authy / 2024 Authy-again attack pattern), purchase premium-rate numbers against your credit, and read the full call/text metadata history. There is no legitimate reason for a live Twilio auth-token to appear inline in Ansible. | |
unencrypted_ | HIGH | File Named vault.yml / *_vault.yml Appears Unencrypted | A file whose name suggests it should be vaulted (vault.yml, group_vars/all/vault.yml, *_vault.yml, *_secrets.yml) is being referenced but its content lacks the $ANSIBLE_VAULT; header. | |
us_ | HIGH | US SSN, UK NINO, Or Canadian SIN National-ID Inline Literal In Playbook | A task contains a literal matching a US Social Security Number ([0-9]{3}-[0-9]{2}-[0-9]{4} excluding obvious non-SSN patterns like 000-, 666-, 9XX-), UK National Insurance Number ([A-CEGHJ-PR-TW-Z]{2}[0-9]{6}[A-D]), Canadian Social Insurance Number (9 digits with Luhn), or Australian TFN ([0-9]{3}\s?[0-9]{3}\s?[0-9]{3} with Luhn). Embedding any national-ID in configuration management is a GDPR Art. 5 / HIPAA §164.514(b) / CCPA §1798.140(v) data-minimisation violation, and for US SSN specifically a direct trigger for state breach-notification obligations in 50 US states. This rule deliberately excludes the well-known test SSN 219-09-9999 (Woolworth wallet), 078-05-1120 (Hilda Schrader Whitcher case), and any 000-/666-/9XX- non-issued ranges. | |
vault_ | HIGH | Vault Password Exported as Literal in Playbook | ANSIBLE_VAULT_PASSWORD / ANSIBLE_VAULT_PASSWORD_FILE is assigned a literal value inside a playbook (as a var, env, or set_fact) - defeats the purpose of vaulting by storing the password in plaintext where the vault data is. | |
youtube_ | HIGH | YouTube API Key | Matches a YouTube Data API key (AIza + 35 chars). The key shares the AIza prefix with other Google APIs and is rotated via Google Cloud Console credentials. | |
ask_ | MEDIUM | CI Script Skips Vault Authentication (–vault-password-file | A shell task or CI script pipes a one-liner password into –vault-password-file via a writable temp location (/tmp, $RUNNER_TEMP) - the vault password lands on disk unencrypted during the run. | |
base64_ | MEDIUM | Base64-like Secret | A secret/key/token/password string is set to a 40+ char base64 literal. The shape commonly indicates a leaked binary key encoded for YAML. | |
hex_ | MEDIUM | Hexadecimal Secret | A secret/key/token/password string is set to a 32+ char hex literal. Hex strings of this length are typical AES or HMAC keys leaked into source. | |
uuid_ | MEDIUM | UUID-like Secret | A UUID-formatted string stored next to a credential-keyword (api_key, secret, auth_token, password). Many services issue credentials as UUIDs (Heroku API keys, Splunk HEC tokens, PagerDuty routing keys). A hardcoded UUID in a file described as a key/secret is therefore likely a real credential - but a UUID stored under the JSON field _key / id / _id is a document primary key, not a credential, and is NOT flagged. | |
vault_ | MEDIUM | Hardcoded vault_identity_list Exposes Vault IDs | vault_identity_list in ansible.cfg enumerates vault IDs with a hardcoded password file path - concentrates every vault’s key in one line, maximising blast radius if that file is leaked. | |
hardcoded_ | LOW | Well-Known Default Account Used | A task connects as one of the highest-risk well-known default accounts (root, administrator, Administrator, sa). These are the top targets for credential stuffing, online password brute-force, and MFA-fatigue attacks because they exist on virtually every deployment. This is an INFORMATIONAL hint, not a secret leak - knowing the username is half the guess. The username itself isn’t confidential; the pairing with an unrotated vendor-default password is. Use a named service principal (ansible-deploy, ci-runner) scoped to only the required privileges and rotate from the vendor default. A hardcoded username is only suspicious when paired with a hardcoded password - see hardcoded_password / hardcoded_credentials / inventory_group_vars_all_contains_plaintext_secret for the actual secret-leak signals. |