diff --git a/docs-site/docs/usage/basic-scanning.md b/docs-site/docs/usage/basic-scanning.md index 17bdde2..4e73176 100644 --- a/docs-site/docs/usage/basic-scanning.md +++ b/docs-site/docs/usage/basic-scanning.md @@ -425,11 +425,31 @@ kingfisher scan ./my-project \ ### Project configuration file (`kingfisher.yaml`) Most `kingfisher scan` flags can be set as project defaults via a -`kingfisher.yaml` file in the repo root (or any ancestor directory). CLI -flags always win; config values fill in defaults. Lists are concatenated. +`kingfisher.yaml` file. CLI flags always win; config values fill in +defaults. Lists are concatenated. + +The config file is **never auto-discovered** — pass `--config FILE` +explicitly or it is not loaded. + +**Step 1 — generate the config from your existing CLI command** (don't +write the YAML by hand): + +```bash +kingfisher config init \ + --confidence high \ + --redact \ + --exclude vendor/ \ + --exclude '**/node_modules/**' \ + --format sarif \ + --output ./kingfisher.sarif \ + --alert-webhook https://hooks.slack.com/services/T0/B0/AAA \ + > kingfisher.yaml +``` + +The resulting `kingfisher.yaml`: ```yaml -# kingfisher.yaml +# kingfisher.yaml — generated by `kingfisher config init`. scan: confidence: high redact: true @@ -443,21 +463,19 @@ filters: alerts: webhooks: - url: https://hooks.slack.com/services/T0/B0/AAA - format: slack ``` +**Step 2 — run the scan, passing the config explicitly:** + ```bash -kingfisher scan . # auto-discovers ./kingfisher.yaml -kingfisher scan . --config /etc/kf.yaml # explicit path +kingfisher scan . --config ./kingfisher.yaml ``` -Don't write the YAML by hand. If you already have a long `kingfisher scan` -command, run the same flags under `kingfisher config init` to generate it: +You can override any config value on the CLI for a single run: ```bash -kingfisher config init \ - --confidence high --redact --exclude vendor/ --format sarif \ - > kingfisher.yaml +kingfisher scan . --config ./kingfisher.yaml --confidence low +# scan.confidence: high in YAML → CLI flag wins, runs at low confidence ``` See [`docs/CONFIG.md`](../usage/configuration.md) for the full schema and precedence rules. @@ -728,6 +746,60 @@ kingfisher scan https://github.com/org/repo.git --repo-artifacts KF_GITHUB_TOKEN="ghp_…" kingfisher scan https://github.com/org/private_repo.git --repo-artifacts ``` +### Scan a GitHub Enterprise / self-hosted GitHub instance + +For GitHub Enterprise Server (GHES) or any self-hosted GitHub install, you +need two flags: + +- `--github-api-url ` — points the **enumeration / clone** flow at the + custom API root (typically `https://ghe.example.com/api/v3/`). +- `--endpoint github=` — points the **token validation / revocation** + flow at the same instance, so any GitHub PATs Kingfisher discovers in the + scanned source are checked against your GHE rather than `api.github.com`. + +```bash +# 1. Scan every org repo on GHE and validate discovered tokens against the same instance +KF_GITHUB_TOKEN="ghp_…" kingfisher scan github \ + --organization my-org \ + --github-api-url https://ghe.corp.example.com/api/v3/ \ + --endpoint github=https://ghe.corp.example.com + +# 2. Scan a single GHE repo by URL (positional target) +KF_GITHUB_TOKEN="ghp_…" kingfisher scan https://ghe.corp.example.com/org/repo.git \ + --endpoint github=https://ghe.corp.example.com + +# 3. Scan ALL orgs on a GHE instance (requires non-default --github-api-url) +KF_GITHUB_TOKEN="ghp_…" kingfisher scan github \ + --all-orgs \ + --github-api-url https://ghe.corp.example.com/api/v3/ \ + --endpoint github=https://ghe.corp.example.com + +# 4. GHE on a private network — add --allow-internal-ips so the validator +# can reach RFC1918 / loopback hosts (SSRF guard is on by default). +KF_GITHUB_TOKEN="ghp_…" kingfisher scan github \ + --organization my-org \ + --github-api-url https://ghe.internal/api/v3/ \ + --endpoint github=https://ghe.internal \ + --allow-internal-ips + +# 5. Validate a single PAT against GHE without scanning anything +kingfisher validate --rule github \ + --endpoint github=https://ghe.corp.example.com \ + "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +# 6. Revoke (delete) a confirmed-leaked PAT against GHE +kingfisher revoke --rule github \ + --endpoint github=https://ghe.corp.example.com \ + "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +``` + +> **Why two URLs?** `--github-api-url` is the GHE *cloning* root that +> Kingfisher walks to enumerate orgs, repos, and contributors. +> `--endpoint github=…` is the *validator* root used to live-check +> discovered tokens. They are usually the same host, but they're separate +> flags because some deployments front-load auth (an SSO portal for repo +> access vs. a direct API endpoint for token validation). + --- ## GitLab @@ -788,6 +860,99 @@ kingfisher scan https://gitlab.com/group/project.git --repo-artifacts KF_GITLAB_TOKEN="glpat-…" kingfisher scan https://gitlab.com/group/private_project.git --repo-artifacts ``` +### Scan a self-hosted (Omnibus / Cloud Native) GitLab instance + +For GitLab self-hosted (Omnibus, Helm, or Cloud Native), pair the +enumeration flag with a matching validation endpoint, just like with GHE: + +- `--gitlab-api-url ` — points the **enumeration / clone** flow at + the custom GitLab root (typically `https://gitlab.example.com/`). +- `--endpoint gitlab=` — points the **token validation / revocation** + flow at the same instance, so any GitLab PATs found in the scanned + source are checked against your self-hosted GitLab rather than + `gitlab.com`. + +```bash +# 1. Scan a self-hosted group and validate discovered tokens against the same instance +KF_GITLAB_TOKEN="glpat-…" kingfisher scan gitlab \ + --group my-group \ + --include-subgroups \ + --gitlab-api-url https://gitlab.corp.example.com/ \ + --endpoint gitlab=https://gitlab.corp.example.com + +# 2. Scan a single self-hosted GitLab project by URL +KF_GITLAB_TOKEN="glpat-…" kingfisher scan https://gitlab.corp.example.com/group/project.git \ + --endpoint gitlab=https://gitlab.corp.example.com + +# 3. Scan ALL groups on a self-hosted GitLab (requires non-default --gitlab-api-url) +KF_GITLAB_TOKEN="glpat-…" kingfisher scan gitlab \ + --all-groups \ + --gitlab-api-url https://gitlab.corp.example.com/ \ + --endpoint gitlab=https://gitlab.corp.example.com + +# 4. Self-hosted GitLab on a private network — add --allow-internal-ips so +# the validator can reach RFC1918 / loopback hosts. +KF_GITLAB_TOKEN="glpat-…" kingfisher scan gitlab \ + --group my-group \ + --gitlab-api-url https://gitlab.internal/ \ + --endpoint gitlab=https://gitlab.internal \ + --allow-internal-ips + +# 5. Validate a single PAT against self-hosted GitLab without scanning anything +kingfisher validate --rule gitlab \ + --endpoint gitlab=https://gitlab.corp.example.com \ + "glpat-xxxxxxxxxxxxxxxxxxxx" + +# 6. Revoke (delete) a confirmed-leaked PAT against self-hosted GitLab +kingfisher revoke --rule gitlab \ + --endpoint gitlab=https://gitlab.corp.example.com \ + "glpat-xxxxxxxxxxxxxxxxxxxx" +``` + +### Many endpoints at once: `--endpoint-config` + +If you maintain a fleet of self-hosted instances (GHE, self-hosted GitLab, +Gitea, Jira DC, Confluence, Artifactory), put them in a single YAML file +and reference it instead of repeating `--endpoint` on every command: + +```yaml +# kingfisher-endpoints.yml +endpoints: + github: https://ghe.corp.example.com + gitlab: https://gitlab.corp.example.com + gitea: https://gitea.corp.example.com + jira: https://jira.corp.example.com + confluence: https://wiki.corp.example.com + artifactory: http://artifactory.internal:8081 +``` + +```bash +KF_GITHUB_TOKEN="ghp_…" KF_GITLAB_TOKEN="glpat-…" kingfisher scan github \ + --organization my-org \ + --github-api-url https://ghe.corp.example.com/api/v3/ \ + --endpoint-config ./kingfisher-endpoints.yml \ + --allow-internal-ips +``` + +### Tip: bake the endpoints into `kingfisher.yaml` + +Once you've worked out the right flags, capture them as project defaults +so every scan uses the same config: + +```bash +kingfisher config init \ + --github-api-url https://ghe.corp.example.com/api/v3/ \ + --gitlab-api-url https://gitlab.corp.example.com/ \ + --endpoint github=https://ghe.corp.example.com \ + --endpoint gitlab=https://gitlab.corp.example.com \ + --allow-internal-ips \ + > kingfisher.yaml + +# Then every scan inherits the same self-hosted defaults: +KF_GITHUB_TOKEN="ghp_…" kingfisher scan github --organization my-org \ + --config ./kingfisher.yaml +``` + ### List GitLab repositories ```bash diff --git a/docs-site/docs/usage/integrations.md b/docs-site/docs/usage/integrations.md index bdd12e2..634fecc 100644 --- a/docs-site/docs/usage/integrations.md +++ b/docs-site/docs/usage/integrations.md @@ -219,8 +219,9 @@ KF_GITHUB_TOKEN="ghp_…" kingfisher scan https://github.com/org/private_repo.gi For GitHub Enterprise Server (GHES) or any self-hosted GitHub install, you need two flags: -- `--github-api-url ` — points the **enumeration / clone** flow at the - custom API root (typically `https://ghe.example.com/api/v3/`). +- `--api-url ` (on `kingfisher scan github`) — points the + **enumeration / clone** flow at the custom API root (typically + `https://ghe.example.com/api/v3/`). - `--endpoint github=` — points the **token validation / revocation** flow at the same instance, so any GitHub PATs Kingfisher discovers in the scanned source are checked against your GHE rather than `api.github.com`. @@ -229,43 +230,43 @@ need two flags: # 1. Scan every org repo on GHE and validate discovered tokens against the same instance KF_GITHUB_TOKEN="ghp_…" kingfisher scan github \ --organization my-org \ - --github-api-url https://ghe.corp.example.com/api/v3/ \ + --api-url https://ghe.corp.example.com/api/v3/ \ --endpoint github=https://ghe.corp.example.com # 2. Scan a single GHE repo by URL (positional target) KF_GITHUB_TOKEN="ghp_…" kingfisher scan https://ghe.corp.example.com/org/repo.git \ --endpoint github=https://ghe.corp.example.com -# 3. Scan ALL orgs on a GHE instance (requires non-default --github-api-url) +# 3. Scan ALL orgs on a GHE instance (requires non-default --api-url) KF_GITHUB_TOKEN="ghp_…" kingfisher scan github \ --all-orgs \ - --github-api-url https://ghe.corp.example.com/api/v3/ \ + --api-url https://ghe.corp.example.com/api/v3/ \ --endpoint github=https://ghe.corp.example.com # 4. GHE on a private network — add --allow-internal-ips so the validator # can reach RFC1918 / loopback hosts (SSRF guard is on by default). KF_GITHUB_TOKEN="ghp_…" kingfisher scan github \ --organization my-org \ - --github-api-url https://ghe.internal/api/v3/ \ + --api-url https://ghe.internal/api/v3/ \ --endpoint github=https://ghe.internal \ --allow-internal-ips # 5. Validate a single PAT against GHE without scanning anything kingfisher validate --rule github \ --endpoint github=https://ghe.corp.example.com \ - "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "" # 6. Revoke (delete) a confirmed-leaked PAT against GHE kingfisher revoke --rule github \ --endpoint github=https://ghe.corp.example.com \ - "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "" ``` -`--github-api-url` is the GHE *cloning* root that Kingfisher walks to -enumerate orgs, repos, and contributors. `--endpoint github=…` is the -*validator* root used to live-check discovered tokens. They are usually the -same host, but they're separate flags because some deployments front-load -auth (an SSO portal for repo access vs. a direct API endpoint for token +`--api-url` is the GHE *cloning* root that Kingfisher walks to enumerate +orgs, repos, and contributors. `--endpoint github=…` is the *validator* +root used to live-check discovered tokens. They are usually the same host, +but they're separate flags because some deployments front-load auth (an +SSO portal for repo access vs. a direct API endpoint for token validation). ## GitLab @@ -343,8 +344,9 @@ KF_GITLAB_TOKEN="glpat-…" kingfisher scan https://gitlab.com/group/private_pro For GitLab self-hosted (Omnibus, Helm, or Cloud Native), pair the enumeration flag with a matching validation endpoint: -- `--gitlab-api-url ` — points the **enumeration / clone** flow at - the custom GitLab root (typically `https://gitlab.example.com/`). +- `--api-url ` (on `kingfisher scan gitlab`) — points the + **enumeration / clone** flow at the custom GitLab root (typically + `https://gitlab.example.com/`). - `--endpoint gitlab=` — points the **token validation / revocation** flow at the same instance, so any GitLab PATs found in the scanned source are checked against your self-hosted GitLab rather than @@ -355,36 +357,36 @@ enumeration flag with a matching validation endpoint: KF_GITLAB_TOKEN="glpat-…" kingfisher scan gitlab \ --group my-group \ --include-subgroups \ - --gitlab-api-url https://gitlab.corp.example.com/ \ + --api-url https://gitlab.corp.example.com/ \ --endpoint gitlab=https://gitlab.corp.example.com # 2. Scan a single self-hosted GitLab project by URL KF_GITLAB_TOKEN="glpat-…" kingfisher scan https://gitlab.corp.example.com/group/project.git \ --endpoint gitlab=https://gitlab.corp.example.com -# 3. Scan ALL groups on a self-hosted GitLab (requires non-default --gitlab-api-url) +# 3. Scan ALL groups on a self-hosted GitLab (requires non-default --api-url) KF_GITLAB_TOKEN="glpat-…" kingfisher scan gitlab \ --all-groups \ - --gitlab-api-url https://gitlab.corp.example.com/ \ + --api-url https://gitlab.corp.example.com/ \ --endpoint gitlab=https://gitlab.corp.example.com # 4. Self-hosted GitLab on a private network — add --allow-internal-ips so # the validator can reach RFC1918 / loopback hosts. KF_GITLAB_TOKEN="glpat-…" kingfisher scan gitlab \ --group my-group \ - --gitlab-api-url https://gitlab.internal/ \ + --api-url https://gitlab.internal/ \ --endpoint gitlab=https://gitlab.internal \ --allow-internal-ips # 5. Validate a single PAT against self-hosted GitLab without scanning anything kingfisher validate --rule gitlab \ --endpoint gitlab=https://gitlab.corp.example.com \ - "glpat-xxxxxxxxxxxxxxxxxxxx" + "" # 6. Revoke (delete) a confirmed-leaked PAT against self-hosted GitLab kingfisher revoke --rule gitlab \ --endpoint gitlab=https://gitlab.corp.example.com \ - "glpat-xxxxxxxxxxxxxxxxxxxx" + "" ``` ### Many endpoints at once: `--endpoint-config` @@ -407,7 +409,7 @@ endpoints: ```bash KF_GITHUB_TOKEN="ghp_…" KF_GITLAB_TOKEN="glpat-…" kingfisher scan github \ --organization my-org \ - --github-api-url https://ghe.corp.example.com/api/v3/ \ + --api-url https://ghe.corp.example.com/api/v3/ \ --endpoint-config ./kingfisher-endpoints.yml \ --allow-internal-ips ``` @@ -716,10 +718,10 @@ To use basic authentication instead, also set `KF_CONFLUENCE_USER` to your Confl ### Scan Slack messages matching a search query ```bash -KF_SLACK_TOKEN="xoxp-1234..." kingfisher scan slack "from:username has:link" \ +KF_SLACK_TOKEN="" kingfisher scan slack "from:username has:link" \ --max-results 1000 -KF_SLACK_TOKEN="xoxp-1234..." kingfisher scan slack "akia" \ +KF_SLACK_TOKEN="" kingfisher scan slack "akia" \ --max-results 1000 ``` @@ -805,7 +807,7 @@ The token is sent as the `X-Api-Key` header. Either `KF_POSTMAN_TOKEN` or `POSTM > Top-level `kingfisher scan --postman-*` flags remain accepted as hidden aliases for backward compatibility, but new usage should prefer the `kingfisher scan postman` subcommand shown above. -**Out of scope:** Postman Vault secrets are client-side and not reachable via the API. The Postman API Network does not expose a search endpoint; supply specific public-workspace IDs via `--postman-workspace` to scan public surfaces. +**Out of scope:** Postman Vault secrets are client-side and not reachable via the API. The Postman API Network does not expose a search endpoint; supply specific public-workspace IDs via `kingfisher scan postman --workspace` to scan public surfaces. ## Environment Variables diff --git a/docs/INTEGRATIONS.md b/docs/INTEGRATIONS.md index d924257..ebe2729 100644 --- a/docs/INTEGRATIONS.md +++ b/docs/INTEGRATIONS.md @@ -211,6 +211,61 @@ kingfisher scan https://github.com/org/repo.git --repo-artifacts KF_GITHUB_TOKEN="ghp_…" kingfisher scan https://github.com/org/private_repo.git --repo-artifacts ``` +### Scan a GitHub Enterprise / self-hosted GitHub instance + +For GitHub Enterprise Server (GHES) or any self-hosted GitHub install, you +need two flags: + +- `--api-url ` (on `kingfisher scan github`) — points the + **enumeration / clone** flow at the custom API root (typically + `https://ghe.example.com/api/v3/`). +- `--endpoint github=` — points the **token validation / revocation** + flow at the same instance, so any GitHub PATs Kingfisher discovers in the + scanned source are checked against your GHE rather than `api.github.com`. + +```bash +# 1. Scan every org repo on GHE and validate discovered tokens against the same instance +KF_GITHUB_TOKEN="ghp_…" kingfisher scan github \ + --organization my-org \ + --api-url https://ghe.corp.example.com/api/v3/ \ + --endpoint github=https://ghe.corp.example.com + +# 2. Scan a single GHE repo by URL (positional target) +KF_GITHUB_TOKEN="ghp_…" kingfisher scan https://ghe.corp.example.com/org/repo.git \ + --endpoint github=https://ghe.corp.example.com + +# 3. Scan ALL orgs on a GHE instance (requires non-default --api-url) +KF_GITHUB_TOKEN="ghp_…" kingfisher scan github \ + --all-orgs \ + --api-url https://ghe.corp.example.com/api/v3/ \ + --endpoint github=https://ghe.corp.example.com + +# 4. GHE on a private network — add --allow-internal-ips so the validator +# can reach RFC1918 / loopback hosts (SSRF guard is on by default). +KF_GITHUB_TOKEN="ghp_…" kingfisher scan github \ + --organization my-org \ + --api-url https://ghe.internal/api/v3/ \ + --endpoint github=https://ghe.internal \ + --allow-internal-ips + +# 5. Validate a single PAT against GHE without scanning anything +kingfisher validate --rule github \ + --endpoint github=https://ghe.corp.example.com \ + "" + +# 6. Revoke (delete) a confirmed-leaked PAT against GHE +kingfisher revoke --rule github \ + --endpoint github=https://ghe.corp.example.com \ + "" +``` + +`--api-url` is the GHE *cloning* root that Kingfisher walks to enumerate +orgs, repos, and contributors. `--endpoint github=…` is the *validator* +root used to live-check discovered tokens. They are usually the same host, +but they're separate flags because some deployments front-load auth (an +SSO portal for repo access vs. a direct API endpoint for token +validation). + ## GitLab ### Scan GitLab group (requires `KF_GITLAB_TOKEN`) @@ -281,6 +336,100 @@ kingfisher scan https://gitlab.com/group/project.git --repo-artifacts KF_GITLAB_TOKEN="glpat-…" kingfisher scan https://gitlab.com/group/private_project.git --repo-artifacts ``` +### Scan a self-hosted (Omnibus / Cloud Native) GitLab instance + +For GitLab self-hosted (Omnibus, Helm, or Cloud Native), pair the +enumeration flag with a matching validation endpoint: + +- `--api-url ` (on `kingfisher scan gitlab`) — points the + **enumeration / clone** flow at the custom GitLab root (typically + `https://gitlab.example.com/`). +- `--endpoint gitlab=` — points the **token validation / revocation** + flow at the same instance, so any GitLab PATs found in the scanned + source are checked against your self-hosted GitLab rather than + `gitlab.com`. + +```bash +# 1. Scan a self-hosted group and validate discovered tokens against the same instance +KF_GITLAB_TOKEN="glpat-…" kingfisher scan gitlab \ + --group my-group \ + --include-subgroups \ + --api-url https://gitlab.corp.example.com/ \ + --endpoint gitlab=https://gitlab.corp.example.com + +# 2. Scan a single self-hosted GitLab project by URL +KF_GITLAB_TOKEN="glpat-…" kingfisher scan https://gitlab.corp.example.com/group/project.git \ + --endpoint gitlab=https://gitlab.corp.example.com + +# 3. Scan ALL groups on a self-hosted GitLab (requires non-default --api-url) +KF_GITLAB_TOKEN="glpat-…" kingfisher scan gitlab \ + --all-groups \ + --api-url https://gitlab.corp.example.com/ \ + --endpoint gitlab=https://gitlab.corp.example.com + +# 4. Self-hosted GitLab on a private network — add --allow-internal-ips so +# the validator can reach RFC1918 / loopback hosts. +KF_GITLAB_TOKEN="glpat-…" kingfisher scan gitlab \ + --group my-group \ + --api-url https://gitlab.internal/ \ + --endpoint gitlab=https://gitlab.internal \ + --allow-internal-ips + +# 5. Validate a single PAT against self-hosted GitLab without scanning anything +kingfisher validate --rule gitlab \ + --endpoint gitlab=https://gitlab.corp.example.com \ + "" + +# 6. Revoke (delete) a confirmed-leaked PAT against self-hosted GitLab +kingfisher revoke --rule gitlab \ + --endpoint gitlab=https://gitlab.corp.example.com \ + "" +``` + +### Many endpoints at once: `--endpoint-config` + +If you maintain a fleet of self-hosted instances (GHE, self-hosted GitLab, +Gitea, Jira DC, Confluence, Artifactory), put them in a single YAML file +and reference it instead of repeating `--endpoint` on every command: + +```yaml +# kingfisher-endpoints.yml +endpoints: + github: https://ghe.corp.example.com + gitlab: https://gitlab.corp.example.com + gitea: https://gitea.corp.example.com + jira: https://jira.corp.example.com + confluence: https://wiki.corp.example.com + artifactory: http://artifactory.internal:8081 +``` + +```bash +KF_GITHUB_TOKEN="ghp_…" KF_GITLAB_TOKEN="glpat-…" kingfisher scan github \ + --organization my-org \ + --api-url https://ghe.corp.example.com/api/v3/ \ + --endpoint-config ./kingfisher-endpoints.yml \ + --allow-internal-ips +``` + +### Tip: bake the endpoints into `kingfisher.yaml` + +Once you've worked out the right flags, capture them as project defaults +so every scan uses the same config: + +```bash +kingfisher config init \ + --github-api-url https://ghe.corp.example.com/api/v3/ \ + --gitlab-api-url https://gitlab.corp.example.com/ \ + --endpoint github=https://ghe.corp.example.com \ + --endpoint gitlab=https://gitlab.corp.example.com \ + --allow-internal-ips \ + > kingfisher.yaml + +# Then every scan inherits the same self-hosted defaults: +KF_GITHUB_TOKEN="ghp_…" kingfisher scan github --organization my-org \ + --config ./kingfisher.yaml +``` + ### List GitLab repositories ```bash @@ -566,10 +715,10 @@ To use basic authentication instead, also set `KF_CONFLUENCE_USER` to your Confl ### Scan Slack messages matching a search query ```bash -KF_SLACK_TOKEN="xoxp-1234..." kingfisher scan slack "from:username has:link" \ +KF_SLACK_TOKEN="" kingfisher scan slack "from:username has:link" \ --max-results 1000 -KF_SLACK_TOKEN="xoxp-1234..." kingfisher scan slack "akia" \ +KF_SLACK_TOKEN="" kingfisher scan slack "akia" \ --max-results 1000 ``` @@ -655,7 +804,7 @@ The token is sent as the `X-Api-Key` header. Either `KF_POSTMAN_TOKEN` or `POSTM > Top-level `kingfisher scan --postman-*` flags remain accepted as hidden aliases for backward compatibility, but new usage should prefer the `kingfisher scan postman` subcommand shown above. -**Out of scope:** Postman Vault secrets are client-side and not reachable via the API. The Postman API Network does not expose a search endpoint; supply specific public-workspace IDs via `--postman-workspace` to scan public surfaces. +**Out of scope:** Postman Vault secrets are client-side and not reachable via the API. The Postman API Network does not expose a search endpoint; supply specific public-workspace IDs via `kingfisher scan postman --workspace` to scan public surfaces. ## Environment Variables diff --git a/src/main.rs b/src/main.rs index dd04318..9d6933e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -346,6 +346,24 @@ fn apply_config( } } + /// Like `config_wins`, but also inspects a nested provider subcommand's + /// `--api-url` flag. The github/gitlab provider subcommands carry their + /// own `api_url` arg (id `api_url`) that gets propagated to + /// `scan_args.input_specifier_args.{github,gitlab}_api_url` in + /// `into_operation()`. If that nested flag was user-supplied, the config + /// must NOT clobber it. + fn api_url_config_wins( + matches: Option<&clap::ArgMatches>, + outer_id: &str, + subcommand: &str, + ) -> bool { + if !config_wins(matches, outer_id) { + return false; + } + let sub = matches.and_then(|m| m.subcommand_matches(subcommand)); + config_wins(sub, "api_url") + } + // ---------- Filters: existing v1 list-typed merges ---------------------- scan_args.skip_word.extend(cfg.filters.skip_words.iter().cloned()); scan_args.skip_regex.extend(cfg.filters.skip_regex.iter().cloned()); @@ -631,14 +649,21 @@ fn apply_config( // field. parse_str() already validated this — `unwrap_or_default()` // would mask a real config bug, so we re-parse and *fail loud* if the // string somehow does not parse here. + // + // The provider subcommands (`scan github`, `scan gitlab`) expose their + // own `--api-url` flag whose value is propagated into the same runtime + // field by `into_operation()`. `api_url_config_wins` checks both the + // outer hidden alias and the nested subcommand flag so an explicit + // `kingfisher scan github --api-url ...` is never overridden by the + // config file. if let Some(u) = &cfg.git.github_api_url - && config_wins(scan_matches, "github_api_url") + && api_url_config_wins(scan_matches, "github_api_url", "github") && let Ok(parsed) = url::Url::parse(u) { scan_args.input_specifier_args.github_api_url = parsed; } if let Some(u) = &cfg.git.gitlab_api_url - && config_wins(scan_matches, "gitlab_api_url") + && api_url_config_wins(scan_matches, "gitlab_api_url", "gitlab") && let Ok(parsed) = url::Url::parse(u) { scan_args.input_specifier_args.gitlab_api_url = parsed; @@ -2268,4 +2293,95 @@ global: assert!(global_args.allow_internal_ips); assert_eq!(global_args.endpoint.len(), 1); } + + /// Regression test: an explicit `--api-url` on the `scan github` + /// subcommand must beat `git.github_api_url` from the config file. The + /// flag lives on `GithubScanArgs` (id `api_url`), not on the outer scan + /// command — checking only the outer matches misses it and the config + /// silently overrode the CLI value. + #[test] + fn github_subcommand_api_url_beats_config() { + let yaml = r#" +git: + github_api_url: https://ghe-from-config.example.com/api/v3/ +"#; + let cfg = parse_str(yaml).unwrap(); + let (args, matches) = parse(&[ + "kingfisher", + "scan", + "github", + "--organization", + "my-org", + "--api-url", + "https://ghe-from-cli.example.com/api/v3/", + ]); + let mut global_args = args.global_args.clone(); + let mut scan_args = into_scan(args); + super::apply_config( + &mut scan_args, + &mut global_args, + &cfg, + matches.subcommand_matches("scan"), + ); + assert_eq!( + scan_args.input_specifier_args.github_api_url.as_str(), + "https://ghe-from-cli.example.com/api/v3/", + ); + } + + /// And the inverse: when the user did NOT pass `--api-url` at all, + /// `git.github_api_url` from the config should still win over the + /// built-in default `https://api.github.com/`. + #[test] + fn github_config_wins_when_subcommand_api_url_default() { + let yaml = r#" +git: + github_api_url: https://ghe-from-config.example.com/api/v3/ +"#; + let cfg = parse_str(yaml).unwrap(); + let (args, matches) = parse(&["kingfisher", "scan", "github", "--organization", "my-org"]); + let mut global_args = args.global_args.clone(); + let mut scan_args = into_scan(args); + super::apply_config( + &mut scan_args, + &mut global_args, + &cfg, + matches.subcommand_matches("scan"), + ); + assert_eq!( + scan_args.input_specifier_args.github_api_url.as_str(), + "https://ghe-from-config.example.com/api/v3/", + ); + } + + /// Same precedence story for `scan gitlab --api-url`. + #[test] + fn gitlab_subcommand_api_url_beats_config() { + let yaml = r#" +git: + gitlab_api_url: https://gitlab-from-config.example.com/ +"#; + let cfg = parse_str(yaml).unwrap(); + let (args, matches) = parse(&[ + "kingfisher", + "scan", + "gitlab", + "--group", + "my-group", + "--api-url", + "https://gitlab-from-cli.example.com/", + ]); + let mut global_args = args.global_args.clone(); + let mut scan_args = into_scan(args); + super::apply_config( + &mut scan_args, + &mut global_args, + &cfg, + matches.subcommand_matches("scan"), + ); + assert_eq!( + scan_args.input_specifier_args.gitlab_api_url.as_str(), + "https://gitlab-from-cli.example.com/", + ); + } }