Kingfisher’s access map determines the effective identity and blast radius of a credential by authenticating to the target provider and enumerating accessible resources and permissions.
There are two ways to produce access maps:
- During scanning:
kingfisher scan ... --access-map
Kingfisher validates detected secrets and automatically generates access-map entries for supported credential types. - Standalone:
kingfisher access-map <provider> [credential_file]
This reads a credential artifact from disk and maps it directly.
Access mapping runs additional network requests. Only use it when you are authorized to inspect the target account/workspace.
Access map only runs for credential types Kingfisher knows how to authenticate with and enumerate. In the codebase, these map to AccessMapRequest variants recorded from validated findings (see src/scanner/validation.rs).
- Credential: a single GitHub token string (read from a file for
kingfisher access-map github <FILE>). - Token types supported: any token accepted by GitHub’s REST API
Authorizationscheme used by Kingfisher (Authorization: token <TOKEN>), including:- Classic PATs (commonly
ghp_...) - Fine-grained PATs (commonly
github_pat_...) - OAuth / user tokens (various prefixes; GitHub controls these)
- GitHub App tokens (Kingfisher detects
ghu_...andghs_...and uses the installations APIs for richer mapping)
- Classic PATs (commonly
printf '%s' 'ghp_example...' > ./github.token
kingfisher access-map github ./github.token --json-out github.access-map.json- Access map currently uses
https://api.github.comas the API base.
- Credential: a single GitLab token string (read from a file for
kingfisher access-map gitlab <FILE>). - Token types supported: any token accepted by GitLab’s
PRIVATE-TOKENheader (PATs likeglpat-..., plus other GitLab token types that work with that header).
When available, Kingfisher also queries the token-self endpoint for metadata; some token types may not expose token details there.
printf '%s' 'glpat-example...' > ./gitlab.token
kingfisher access-map gitlab ./gitlab.token --json-out gitlab.access-map.json- Access map currently uses
https://gitlab.com/api/v4/as the API base.
- Credential: a single Slack token string (read from a file for
kingfisher access-map slack <FILE>). - Token types supported: tokens accepted by Slack Web API with
Authorization: Bearer <TOKEN>(for examplexoxp-...,xoxb-..., etc.).
Kingfisher derives scopes from thex-oauth-scopesresponse header when Slack returns it.
printf '%s' 'xoxp-example...' > ./slack.token
kingfisher access-map slack ./slack.token --json-out slack.access-map.json- Credential: AWS access key credentials.
- Supported formats for
kingfisher access-map aws <FILE>:- JSON object with case-insensitive support for the following keys:
access_key_id/accessKeyId/aws_access_key_id/AccessKeyIdsecret_access_key/secretAccessKey/aws_secret_access_key/SecretAccessKey- optional
session_token/sessionToken/aws_session_token/SessionToken
- Key/value file containing
KEY=VALUElines (comments allowed with#), supporting:aws_access_key_idoraccess_key_idaws_secret_access_keyorsecret_access_key- optional
aws_session_tokenorsession_token
- JSON object with case-insensitive support for the following keys:
cat > ./aws.json <<'EOF'
{
"access_key_id": "AKIA....",
"secret_access_key": "....",
"session_token": "...."
}
EOF
kingfisher access-map aws ./aws.json --json-out aws.access-map.jsoncat > ./aws.env <<'EOF'
aws_access_key_id=AKIA....
aws_secret_access_key=....
aws_session_token=....
EOF
kingfisher access-map aws ./aws.env --json-out aws.access-map.json- Credential: a Google Cloud service account key JSON file.
kingfisher access-map gcp ./service-account.json --json-out gcp.access-map.json- Credential: a JSON file containing:
storage_account(string)storage_key(string, base64-encoded account key as provided by Azure)
cat > ./azure-storage.json <<'EOF'
{
"storage_account": "mystorageacct",
"storage_key": "base64=="
}
EOF
kingfisher access-map azure ./azure-storage.json --json-out azure.access-map.jsonAzure DevOps access mapping is supported when a validated Azure DevOps PAT is discovered during scanning (the access-map record includes both the PAT and the organization). At the moment, there is no standalone kingfisher access-map azure-devops ... provider flag.
- Credential: a single Postgres connection URI string (read from a file).
printf '%s' 'postgres://user:pass@db.example.com:5432/mydb' > ./postgres.uri
kingfisher access-map postgres ./postgres.uri --json-out postgres.access-map.json- Credential: a single MongoDB connection URI string (read from a file), including
mongodb+srv://...URIs.
printf '%s' 'mongodb+srv://user:pass@cluster.example.net/?retryWrites=true&w=majority' > ./mongodb.uri
kingfisher access-map mongodb ./mongodb.uri --json-out mongodb.access-map.json- Credential: a single Hugging Face token string (read from a file for
kingfisher access-map huggingface <FILE>). - Token types supported: tokens accepted by the Hugging Face API with
Authorization: Bearer <TOKEN>, including:- User access tokens (commonly
hf_...) - Organization API tokens (commonly
api_org_...)
- User access tokens (commonly
Kingfisher queries the /api/whoami-v2 endpoint to resolve the token identity, role, and organization memberships. It also enumerates models authored by the user to assess the blast radius.
printf '%s' 'hf_example...' > ./huggingface.token
kingfisher access-map huggingface ./huggingface.token --json-out huggingface.access-map.json- Access map uses
https://huggingface.co/apias the API base. - Token role (read, write, admin, fineGrained) is derived from the
authsection of the whoami response when available.
- Credential: a single Gitea token string (read from a file for
kingfisher access-map gitea <FILE>). - Token types supported: any token accepted by Gitea's
Authorization: token <TOKEN>header (personal access tokens).
Kingfisher queries /api/v1/user for identity, enumerates organizations via /api/v1/user/orgs, and lists accessible repositories via /api/v1/user/repos. Repository-level permissions (admin, push, pull) are used to classify risk.
printf '%s' 'your_gitea_pat...' > ./gitea.token
kingfisher access-map gitea ./gitea.token --json-out gitea.access-map.json- Access map currently uses
https://gitea.com/api/v1/as the default API base. - If the token belongs to a site administrator, severity is classified as Critical.
- Credential: a single Bitbucket token string (read from a file for
kingfisher access-map bitbucket <FILE>). - Token types supported: tokens accepted by Bitbucket Cloud's
Authorization: Bearer <TOKEN>header (OAuth access tokens, app passwords, repository access tokens).
Kingfisher queries /2.0/user for identity, enumerates workspace memberships and permissions via /2.0/user/permissions/workspaces, and lists accessible repositories via /2.0/repositories?role=member. Workspace ownership and private repository access are used to classify risk.
printf '%s' 'your_bitbucket_token...' > ./bitbucket.token
kingfisher access-map bitbucket ./bitbucket.token --json-out bitbucket.access-map.json- Access map uses
https://api.bitbucket.org/2.0as the API base. - Workspace owners are classified as High severity.
- Credential: a single Buildkite API token string (read from a file for
kingfisher access-map buildkite <FILE>). - Token types supported: tokens accepted by Buildkite's REST API with
Authorization: Bearer <TOKEN>(API access tokens, commonlybkua_...).
Kingfisher queries /v2/access-token for token metadata and scopes, /v2/user for identity, /v2/organizations for organization memberships, and /v2/organizations/{org}/pipelines for pipeline enumeration. Token scopes and organization access are used to classify risk.
printf '%s' 'bkua_example...' > ./buildkite.token
kingfisher access-map buildkite ./buildkite.token --json-out buildkite.access-map.json- Access map uses
https://api.buildkite.com/v2as the API base. - Tokens with
write_organizationsorwrite_teamsscopes are classified as High severity.
- Credential: a single Harness API key / personal access token (PAT) string (read from a file for
kingfisher access-map harness <FILE>). - Auth header: Harness APIs authenticate via
x-api-key: <TOKEN>(see the Harness API docs).
Kingfisher performs best-effort, read-only enumeration:
- Queries the API key aggregate endpoint for basic token metadata (when available).
- Enumerates organizations via
GET https://app.harness.io/v1/orgsand projects viaGET https://app.harness.io/v1/orgs/{org}/projectswhen the key has permission.
If organizations/projects are not enumerable (scope-limited keys), Kingfisher still produces an access-map record with a conservative severity and a note explaining the limitation.
printf '%s' 'pat.example...' > ./harness.token
kingfisher access-map harness ./harness.token --json-out harness.access-map.json- Access map uses
https://app.harness.ioas the API base.
- Credential: a single OpenAI API key string (read from a file for
kingfisher access-map openai <FILE>). - Token types supported: OpenAI keys accepted by
Authorization: Bearer <TOKEN>(for examplesk-...,sk-proj-...,sk-svcacct-...).
Kingfisher performs read-only scope probing via:
GET https://api.openai.com/v1/modelsto verify Models API read access and infer organization ownership (it does not enumerate or emit individual model resources in the access map).GET https://api.openai.com/v1/mefor token identity metadata when available.GET https://api.openai.com/v1/organization/projectsfor project visibility when the key has permission (best-effort).
printf '%s' 'sk-example...' > ./openai.token
kingfisher access-map openai ./openai.token --json-out openai.access-map.json- Access map uses
https://api.openai.com/v1as the API base.
- Credential: Salesforce access token plus instance domain.
- Supported standalone formats for
kingfisher access-map salesforce <FILE>:- JSON:
token(oraccess_token)instance_url(orinstance), such ashttps://mydomain.my.salesforce.com
- Free-form text containing both:
- a Salesforce access token (
00...!...) - an instance host (
<instance>.my.salesforce.com)
- a Salesforce access token (
- JSON:
Kingfisher performs read-only enumeration via:
GET /services/data/v60.0/limitsto confirm API access and gather account-level API context.GET /services/oauth2/userinfofor identity metadata when available.GET /services/data/v60.0/sobjectsfor visible object metadata (best-effort).
cat > ./salesforce.json <<'EOF'
{
"token": "00DE0X0A0M0PeLE!AQcAQH0dMHEXAMPLE...",
"instance_url": "https://mydomain.my.salesforce.com"
}
EOF
kingfisher access-map salesforce ./salesforce.json --json-out salesforce.access-map.json- Access map currently targets
https://<instance>.my.salesforce.comand API versionv60.0.
- Credential: a single Weights & Biases API key string (read from a file for
kingfisher access-map weightsandbiases <FILE>). - Token types supported:
- Legacy 40-character hex API keys
- New v1 keys (
wandb_v1_...)
Kingfisher performs read-only identity resolution via:
POST https://api.wandb.ai/graphqlwith a GraphQLviewerquery.
printf '%s' 'wandb_v1_example...' > ./wandb.token
kingfisher access-map weightsandbiases ./wandb.token --json-out wandb.access-map.json- Access map uses
https://api.wandb.ai/graphqlas the API endpoint. - W&B key introspection does not currently expose fine-grained scopes in this workflow, so risk is reported conservatively.
- Credential: a Microsoft Teams Incoming Webhook URL (read from a file for
kingfisher access-map microsoftteams <FILE>). - Webhook types supported:
- Legacy Incoming Webhooks (
*.office.com/webhook/...) - Workflow-based webhooks (
*.webhook.office.com/webhookb2/...)
- Legacy Incoming Webhooks (
Kingfisher parses the webhook URL to extract the tenant ID and webhook identity, then sends a benign probe ({"text":""}) to determine whether the webhook is still active. Active webhooks can post messages to the configured Teams channel.
printf '%s' 'https://contoso.webhook.office.com/webhookb2/...' > ./teams.webhook
kingfisher access-map microsoftteams ./teams.webhook --json-out teams.access-map.json- The webhook URL is the credential — it contains the tenant ID and grants write access to a single Teams channel.
- Access map severity is Medium for active webhooks (write-only to one channel) and Low for inactive/removed webhooks.
- The probe request does not post any visible message; Teams responds with HTTP 400 "Text is required" for valid endpoints.
- Access-map entries are only recorded for validated findings.
- Some providers require extra context that Kingfisher infers from the finding context or validation response (for example, Azure DevOps organization name).
- Validated Hugging Face, Gitea, Bitbucket, Buildkite, Harness, OpenAI, Anthropic, Salesforce, Weights & Biases, and Microsoft Teams credentials discovered during scans with
--access-mapare automatically collected and mapped, matching the existing behavior for other platforms.