corgea-cli 1.13.0__tar.gz → 1.14.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/Cargo.lock +1 -1
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/Cargo.toml +1 -1
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/PKG-INFO +1 -1
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/README.md +17 -1
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scan.rs +180 -6
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/blast.rs +22 -1
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/utils/api.rs +445 -92
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_scan_wait_terminal_state.rs +38 -7
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/common/mod.rs +18 -0
- corgea_cli-1.14.0/tests/cloud_commands_e2e/gateway_retry.rs +222 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/main.rs +1 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/common/mod.rs +1 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.codex/hooks.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.github/workflows/npm-publish.yml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.github/workflows/release-binaries.yml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.github/workflows/release.yml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.github/workflows/staging-contract.yml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.github/workflows/test.yml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.gitignore +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/AGENTS.md +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/LICENSE +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/RELEASING.md +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/bin/corgea.js +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/build_release.sh +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/examples/deps_skill.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/harness +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/package.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/pyproject.toml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/scripts/check-linux-binary.sh +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/scripts/npm/bundle-binaries.js +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/skills/corgea/SKILL.md +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/advisories.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/authorize.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/cicd.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/config.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/catalog.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/detect.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/diff.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/ecosystems/evaluate.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/ecosystems/maven.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/ecosystems/mod.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/ecosystems/npm.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/ecosystems/pypi.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/explain.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/findings.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/mod.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/model.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/parse/mod.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/parse/npm_lock.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/parse/python_lock.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/policy.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/report.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/run.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/skill.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/common.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/correctness_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/detect_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/diff_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/explain_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/findings_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/maven_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/mod.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/npm_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/policy_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/pypi_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/report_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/robustness_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/slice0_tests.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/images.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/incremental.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/inspect.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/lib.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/list.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/log.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/main.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/mcp.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/detect.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/exec.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/mod.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/parse.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/render.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/test_support.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/tree.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/uv.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/verdict.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/fortify.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/parsers/checkmarx.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/parsers/coverity.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/parsers/mod.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/parsers/sarif.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/parsers/semgrep.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/setup_hooks.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/skill.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/skip_scan.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/targets.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/utils/generic.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/utils/terminal.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/verify_deps/mod.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/verify_deps/registry.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/vuln_api/mod.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/vuln_api_stub/mod.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/wait.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_advisories.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_bare_install.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_config_errors.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_deps.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_deps_skill.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_exec_fallback.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_install.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_mcp.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_npm_ci.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_provenance.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_remediation.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_scan_include_image.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_scan_sbom.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_tree.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_uv_sync.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_verdict.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/block_on_report.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/inspect.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/scan_incremental.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/scan_list.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/scan_skip.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/upload_wait.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/README.md +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/go-mod-smoke/go.mod +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/go-mod-smoke/go.sum +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-gradle/build.gradle +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-gradle/gradle.lockfile +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven/pom.xml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-alias/pom.xml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-chained/pom.xml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-deep-chain/pom.xml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-managed/pom.xml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-parent/pom.xml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-plugin-deps/pom.xml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-profile-mgmt/pom.xml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-props/pom.xml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-revision/pom.xml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/not-xml-pom.xml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/package-lock.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/package.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/poetry.lock +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/pyproject.toml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/truncated-poetry.lock +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/mixed-monorepo/services/api/requirements.txt +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/mixed-monorepo/services/web/package-lock.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/mixed-monorepo/services/web/package.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-app/.corgea/deps.yml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-app/package-lock.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-app/package.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-monorepo/package-lock.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-monorepo/package.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-monorepo/packages/a/package.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-monorepo/packages/b/package.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-pnpm/package.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-pnpm/pnpm-lock.yaml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-stale/package-lock.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-stale/package.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-transitive/package-lock.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-transitive/package.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-yarn/package.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-yarn/yarn.lock +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-pip-nolock/requirements.txt +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-poetry/poetry.lock +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-poetry/pyproject.toml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-poetry-multi/poetry.lock +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-poetry-multi/pyproject.toml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-uv-requirements/pyproject.toml +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-uv-requirements/requirements.txt +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-uv-requirements/uv.lock +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/README.md +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/check_clean.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/check_malware.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/check_unknown.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/check_vulnerable.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/package_profile.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/package_profile_not_found.json +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/harness_smoke.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/list_code_quality.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/list_resolution.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/logging_e2e.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/vuln_api_contract.rs +0 -0
- {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/wait_resolution.rs +0 -0
|
@@ -39,7 +39,8 @@ third-party report.
|
|
|
39
39
|
|
|
40
40
|
All three exit 1 if the scan fails, printing the reason and the scanners that hit
|
|
41
41
|
problems. A scan that completes with a scanner missing exits 0 with a warning.
|
|
42
|
-
|
|
42
|
+
While waiting, the scan's status is read every 3 seconds. Waiting gives up after
|
|
43
|
+
10 hours; override with `CORGEA_SCAN_TIMEOUT_SECONDS`.
|
|
43
44
|
`--fail`/`--block-on` then wait up to 15 minutes for blocking rules to be
|
|
44
45
|
evaluated; override with `CORGEA_BLOCKING_RULES_TIMEOUT_SECONDS`.
|
|
45
46
|
|
|
@@ -47,6 +48,21 @@ evaluated; override with `CORGEA_BLOCKING_RULES_TIMEOUT_SECONDS`.
|
|
|
47
48
|
trips: both are written before `--fail`/`--block-on` are evaluated, so a scan
|
|
48
49
|
that exits 1 on a blocking rule still leaves its report behind to ingest.
|
|
49
50
|
|
|
51
|
+
### Gateway errors are retried, not surfaced
|
|
52
|
+
|
|
53
|
+
Every Corgea API call the CLI makes — uploads included — replays itself when the
|
|
54
|
+
platform's proxy answers `502 Bad Gateway`, waiting 10s, then 30s, then 50s. A
|
|
55
|
+
request that is still answered 502 after those three retries fails the command
|
|
56
|
+
in the usual way, so a pipeline exits non-zero on a real outage and rides out the
|
|
57
|
+
blips a busy platform produces under parallel scans. Each retry is logged, and
|
|
58
|
+
the count belongs to a single request: any successful call starts the next one
|
|
59
|
+
with the full three retries again.
|
|
60
|
+
|
|
61
|
+
The one place this stops early is `corgea upload`'s per-file source upload: a 502
|
|
62
|
+
that outlives the retries there is taken as the platform being unavailable rather
|
|
63
|
+
than one bad file, so the remaining paths are reported as unsent instead of each
|
|
64
|
+
spending another 90 seconds.
|
|
65
|
+
|
|
50
66
|
### Skipping a re-scan of the same commit
|
|
51
67
|
|
|
52
68
|
A pipeline that re-runs on an unchanged commit can reuse the scan it already
|
|
@@ -6,10 +6,67 @@ use reqwest::header;
|
|
|
6
6
|
use serde_json::Value;
|
|
7
7
|
use std::collections::HashSet;
|
|
8
8
|
use std::io::{self, Read};
|
|
9
|
-
use std::path::Path;
|
|
9
|
+
use std::path::{Path, PathBuf};
|
|
10
10
|
use std::process::Command;
|
|
11
11
|
use uuid::Uuid;
|
|
12
12
|
|
|
13
|
+
const CORGEA_POLICY_FILENAMES: &[&str] = &["corgea.yaml", "corgea.yml"];
|
|
14
|
+
|
|
15
|
+
fn is_regular_file(path: &Path) -> bool {
|
|
16
|
+
!path.is_symlink() && path.is_file()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// `corgea.yaml` / `corgea.yml` under `root`, including a gitignored copy at `root`.
|
|
20
|
+
fn find_corgea_policy_files(root: &Path) -> Vec<String> {
|
|
21
|
+
if !root.is_dir() {
|
|
22
|
+
return Vec::new();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let mut found = Vec::new();
|
|
26
|
+
for name in CORGEA_POLICY_FILENAMES {
|
|
27
|
+
if is_regular_file(&root.join(name)) {
|
|
28
|
+
found.push((*name).to_string());
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let walker = ignore::WalkBuilder::new(root)
|
|
33
|
+
.standard_filters(true)
|
|
34
|
+
.build();
|
|
35
|
+
for result in walker {
|
|
36
|
+
let Ok(entry) = result else {
|
|
37
|
+
continue;
|
|
38
|
+
};
|
|
39
|
+
let path = entry.path();
|
|
40
|
+
if !is_regular_file(path) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
let Some(name) = path.file_name().and_then(|n| n.to_str()) else {
|
|
44
|
+
continue;
|
|
45
|
+
};
|
|
46
|
+
if !CORGEA_POLICY_FILENAMES.contains(&name) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if let Ok(rel) = path.strip_prefix(root) {
|
|
50
|
+
let rel = rel.to_string_lossy().replace('\\', "/");
|
|
51
|
+
if !found.iter().any(|existing| existing == &rel) {
|
|
52
|
+
found.push(rel);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
found.sort();
|
|
57
|
+
found
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn merge_corgea_policy_files(mut paths: Vec<String>, root: &Path) -> Vec<String> {
|
|
61
|
+
for yaml in find_corgea_policy_files(root) {
|
|
62
|
+
if !paths.iter().any(|path| path == &yaml) {
|
|
63
|
+
debug(&format!("Including repo policy file: {yaml}"));
|
|
64
|
+
paths.push(yaml);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
paths
|
|
68
|
+
}
|
|
69
|
+
|
|
13
70
|
pub fn run_command(base_cmd: &String, mut command: Command) -> String {
|
|
14
71
|
match which::which(base_cmd) {
|
|
15
72
|
Ok(_) => {
|
|
@@ -222,6 +279,8 @@ pub fn upload_scan(
|
|
|
222
279
|
save_to_file: bool,
|
|
223
280
|
project_name: Option<String>,
|
|
224
281
|
) -> Option<ScanUploadResult> {
|
|
282
|
+
let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
|
283
|
+
let paths = merge_corgea_policy_files(paths, &cwd);
|
|
225
284
|
let in_ci = running_in_ci();
|
|
226
285
|
let ci_platform = which_ci();
|
|
227
286
|
let github_env_vars = get_github_env_vars();
|
|
@@ -269,8 +328,9 @@ pub fn upload_scan(
|
|
|
269
328
|
let mut uploaded_paths = HashSet::new();
|
|
270
329
|
let mut uploaded_count = 0;
|
|
271
330
|
let mut upload_error_count = 0;
|
|
331
|
+
let mut gateway_gave_up = false;
|
|
272
332
|
|
|
273
|
-
for path in &paths {
|
|
333
|
+
'files: for path in &paths {
|
|
274
334
|
if !Path::new(&path).exists() {
|
|
275
335
|
log::error!(
|
|
276
336
|
"Required file {} not found which is required for the scan, exiting.",
|
|
@@ -295,7 +355,7 @@ pub fn upload_scan(
|
|
|
295
355
|
|
|
296
356
|
while attempts < 3 && !success {
|
|
297
357
|
debug(&format!("POST: {}", src_upload_url));
|
|
298
|
-
let res = utils::api::
|
|
358
|
+
let res = utils::api::send_with_retries("a source file upload", || {
|
|
299
359
|
let form = reqwest::blocking::multipart::Form::new()
|
|
300
360
|
.file("file", fp)
|
|
301
361
|
.expect("Failed to read file");
|
|
@@ -313,6 +373,20 @@ pub fn upload_scan(
|
|
|
313
373
|
"Code upload failed with status: {}. Response body: {}",
|
|
314
374
|
status, body
|
|
315
375
|
));
|
|
376
|
+
// A 502 that outlived the retry schedule is the platform
|
|
377
|
+
// being unavailable, not this one file. Retrying it here
|
|
378
|
+
// would spend the schedule twice over, and walking the
|
|
379
|
+
// remaining paths would spend a fresh 90 seconds on each
|
|
380
|
+
// of them, so stop uploading source files altogether.
|
|
381
|
+
if utils::api::is_gateway_error(status) {
|
|
382
|
+
log::warn!(
|
|
383
|
+
"Failed to upload file {} after the gateway retries: {}",
|
|
384
|
+
path,
|
|
385
|
+
status
|
|
386
|
+
);
|
|
387
|
+
gateway_gave_up = true;
|
|
388
|
+
break 'files;
|
|
389
|
+
}
|
|
316
390
|
log::warn!("Failed to upload file {} {}... retrying", status, path);
|
|
317
391
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
|
318
392
|
attempts += 1;
|
|
@@ -343,6 +417,19 @@ pub fn upload_scan(
|
|
|
343
417
|
}
|
|
344
418
|
}
|
|
345
419
|
|
|
420
|
+
// Everything the aborted walk never attempted still counts as unsent, or
|
|
421
|
+
// the closing summary would report one failure for a whole skipped tree.
|
|
422
|
+
if gateway_gave_up {
|
|
423
|
+
let distinct: HashSet<&String> = paths.iter().collect();
|
|
424
|
+
let unsent = distinct.len() - uploaded_paths.len();
|
|
425
|
+
upload_error_count += unsent;
|
|
426
|
+
log::warn!(
|
|
427
|
+
"Stopped uploading source files: Corgea was still answering 502 after the retries. {} of {} files were not sent.",
|
|
428
|
+
unsent,
|
|
429
|
+
distinct.len()
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
346
433
|
if uploaded_count == 0 {
|
|
347
434
|
log::error!("Failed to upload any files for the scan, exiting.");
|
|
348
435
|
std::process::exit(1);
|
|
@@ -381,7 +468,7 @@ pub fn upload_scan(
|
|
|
381
468
|
index + 1,
|
|
382
469
|
total_chunks
|
|
383
470
|
));
|
|
384
|
-
let response = utils::api::
|
|
471
|
+
let response = utils::api::send_with_retries("a scan report chunk upload", || {
|
|
385
472
|
client
|
|
386
473
|
.post(&scan_upload_url)
|
|
387
474
|
.header(header::CONTENT_TYPE, "application/json")
|
|
@@ -432,7 +519,7 @@ pub fn upload_scan(
|
|
|
432
519
|
last_response.expect("Failed to upload scan.")
|
|
433
520
|
} else {
|
|
434
521
|
debug(&format!("POST: {}", scan_upload_url));
|
|
435
|
-
utils::api::
|
|
522
|
+
utils::api::send_with_retries("the scan report upload", || {
|
|
436
523
|
client
|
|
437
524
|
.post(&scan_upload_url)
|
|
438
525
|
.header(header::CONTENT_TYPE, "application/json")
|
|
@@ -513,7 +600,7 @@ pub fn upload_scan(
|
|
|
513
600
|
if git_config_path.exists() {
|
|
514
601
|
debug("Uploading .git/config");
|
|
515
602
|
debug(&format!("POST: {}", git_config_upload_url));
|
|
516
|
-
let res = utils::api::
|
|
603
|
+
let res = utils::api::send_with_retries("the git config upload", || {
|
|
517
604
|
let form = reqwest::blocking::multipart::Form::new()
|
|
518
605
|
.file("file", git_config_path)
|
|
519
606
|
.expect("Failed to read file");
|
|
@@ -597,6 +684,7 @@ pub fn upload_scan(
|
|
|
597
684
|
#[cfg(test)]
|
|
598
685
|
mod tests {
|
|
599
686
|
use super::*;
|
|
687
|
+
use std::path::Path;
|
|
600
688
|
|
|
601
689
|
#[test]
|
|
602
690
|
fn scan_url_prefers_project_id_when_present() {
|
|
@@ -631,4 +719,90 @@ mod tests {
|
|
|
631
719
|
"https://www.corgea.app/project/corgea_cli-1.0?scan_id=scan-123"
|
|
632
720
|
);
|
|
633
721
|
}
|
|
722
|
+
|
|
723
|
+
fn write_policy(path: &Path) {
|
|
724
|
+
std::fs::write(path, "policies: []\n").unwrap();
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
fn git_repo(root: &Path) {
|
|
728
|
+
std::fs::create_dir(root.join(".git")).unwrap();
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
#[test]
|
|
732
|
+
fn find_corgea_policy_files_picks_yaml_and_yml_by_basename() {
|
|
733
|
+
let root = tempfile::tempdir().unwrap();
|
|
734
|
+
write_policy(&root.path().join("corgea.yaml"));
|
|
735
|
+
std::fs::create_dir(root.path().join("src")).unwrap();
|
|
736
|
+
write_policy(&root.path().join("src/corgea.yml"));
|
|
737
|
+
write_policy(&root.path().join("with-guidance.yaml"));
|
|
738
|
+
|
|
739
|
+
assert_eq!(
|
|
740
|
+
find_corgea_policy_files(root.path()),
|
|
741
|
+
vec!["corgea.yaml".to_string(), "src/corgea.yml".to_string()]
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
#[test]
|
|
746
|
+
fn find_corgea_policy_files_returns_empty_when_none_exist() {
|
|
747
|
+
let root = tempfile::tempdir().unwrap();
|
|
748
|
+
assert!(find_corgea_policy_files(root.path()).is_empty());
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
#[cfg(unix)]
|
|
752
|
+
#[test]
|
|
753
|
+
fn find_corgea_policy_files_skips_symlink_outside_repo() {
|
|
754
|
+
use std::os::unix::fs::symlink;
|
|
755
|
+
let root = tempfile::tempdir().unwrap();
|
|
756
|
+
let secret = tempfile::NamedTempFile::new().unwrap();
|
|
757
|
+
std::fs::write(secret.path(), "secret").unwrap();
|
|
758
|
+
symlink(secret.path(), root.path().join("corgea.yaml")).unwrap();
|
|
759
|
+
|
|
760
|
+
assert!(find_corgea_policy_files(root.path()).is_empty());
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
#[test]
|
|
764
|
+
fn find_corgea_policy_files_skips_gitignored_nested_copies() {
|
|
765
|
+
let root = tempfile::tempdir().unwrap();
|
|
766
|
+
git_repo(root.path());
|
|
767
|
+
std::fs::write(root.path().join(".gitignore"), "node_modules/\n").unwrap();
|
|
768
|
+
write_policy(&root.path().join("corgea.yaml"));
|
|
769
|
+
std::fs::create_dir_all(root.path().join("node_modules/pkg")).unwrap();
|
|
770
|
+
write_policy(&root.path().join("node_modules/pkg/corgea.yaml"));
|
|
771
|
+
|
|
772
|
+
assert_eq!(
|
|
773
|
+
find_corgea_policy_files(root.path()),
|
|
774
|
+
vec!["corgea.yaml".to_string()]
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
#[test]
|
|
779
|
+
fn find_corgea_policy_files_keeps_root_file_even_if_gitignored() {
|
|
780
|
+
let root = tempfile::tempdir().unwrap();
|
|
781
|
+
git_repo(root.path());
|
|
782
|
+
std::fs::write(root.path().join(".gitignore"), "corgea.yaml\n").unwrap();
|
|
783
|
+
write_policy(&root.path().join("corgea.yaml"));
|
|
784
|
+
|
|
785
|
+
assert_eq!(
|
|
786
|
+
find_corgea_policy_files(root.path()),
|
|
787
|
+
vec!["corgea.yaml".to_string()]
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
#[test]
|
|
792
|
+
fn merge_corgea_policy_files_appends_missing_and_skips_duplicates() {
|
|
793
|
+
let root = tempfile::tempdir().unwrap();
|
|
794
|
+
write_policy(&root.path().join("corgea.yaml"));
|
|
795
|
+
|
|
796
|
+
assert_eq!(
|
|
797
|
+
merge_corgea_policy_files(vec!["src/source.py".into()], root.path()),
|
|
798
|
+
vec!["src/source.py".to_string(), "corgea.yaml".to_string()]
|
|
799
|
+
);
|
|
800
|
+
assert_eq!(
|
|
801
|
+
merge_corgea_policy_files(
|
|
802
|
+
vec!["src/source.py".into(), "corgea.yaml".into()],
|
|
803
|
+
root.path()
|
|
804
|
+
),
|
|
805
|
+
vec!["src/source.py".to_string(), "corgea.yaml".to_string()]
|
|
806
|
+
);
|
|
807
|
+
}
|
|
634
808
|
}
|
|
@@ -16,6 +16,15 @@ use std::time::{Duration, Instant};
|
|
|
16
16
|
const SCAN_TIMEOUT_ENV: &str = "CORGEA_SCAN_TIMEOUT_SECONDS";
|
|
17
17
|
const DEFAULT_SCAN_TIMEOUT: Duration = Duration::from_secs(10 * 60 * 60);
|
|
18
18
|
|
|
19
|
+
/// How long to pause between scan status reads.
|
|
20
|
+
///
|
|
21
|
+
/// Scans run for minutes, so reading more often than this only adds load: at
|
|
22
|
+
/// one read a second, a single wait against the default 10-hour budget can
|
|
23
|
+
/// reach 36,000 requests, and a pipeline scanning in parallel multiplies that
|
|
24
|
+
/// by every concurrent wait. Three seconds costs a pipeline no more than a few
|
|
25
|
+
/// seconds of extra latency on the final status.
|
|
26
|
+
const SCAN_POLL_INTERVAL: Duration = Duration::from_secs(3);
|
|
27
|
+
|
|
19
28
|
/// Overrides how long the CI gate waits for blocking rules to be evaluated.
|
|
20
29
|
const BLOCKING_RULES_TIMEOUT_ENV: &str = "CORGEA_BLOCKING_RULES_TIMEOUT_SECONDS";
|
|
21
30
|
|
|
@@ -1034,7 +1043,7 @@ pub fn wait_for_scan(config: &Config, scan_id: &str, budget: WaitBudget) {
|
|
|
1034
1043
|
let mut last_status = String::from("unknown");
|
|
1035
1044
|
|
|
1036
1045
|
let result = loop {
|
|
1037
|
-
thread::sleep(
|
|
1046
|
+
thread::sleep(SCAN_POLL_INTERVAL);
|
|
1038
1047
|
// Every read is capped to what is left of the budget: on the client's
|
|
1039
1048
|
// own timeout a stalled read would otherwise keep us going long past
|
|
1040
1049
|
// the wait the user asked for.
|
|
@@ -1781,6 +1790,18 @@ mod tests {
|
|
|
1781
1790
|
assert_eq!(format_timeout(Duration::from_secs(90)), "90s");
|
|
1782
1791
|
}
|
|
1783
1792
|
|
|
1793
|
+
#[test]
|
|
1794
|
+
fn poll_interval_is_the_documented_one_and_short_against_the_budget() {
|
|
1795
|
+
assert_eq!(SCAN_POLL_INTERVAL, Duration::from_secs(3));
|
|
1796
|
+
// A pause that ever grew toward the budget would spend the whole wait
|
|
1797
|
+
// asleep and report a finished scan long after it finished.
|
|
1798
|
+
assert!(
|
|
1799
|
+
SCAN_POLL_INTERVAL < DEFAULT_SCAN_TIMEOUT / 100,
|
|
1800
|
+
"poll interval {SCAN_POLL_INTERVAL:?} is not short against the \
|
|
1801
|
+
{DEFAULT_SCAN_TIMEOUT:?} budget it runs inside"
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1784
1805
|
#[test]
|
|
1785
1806
|
fn blocking_rules_timeout_outlasts_the_doghouse_wait_windows() {
|
|
1786
1807
|
// This side fails closed on its own deadline, so it must never expire
|