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.
Files changed (184) hide show
  1. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/Cargo.lock +1 -1
  2. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/Cargo.toml +1 -1
  3. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/PKG-INFO +1 -1
  4. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/README.md +17 -1
  5. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scan.rs +180 -6
  6. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/blast.rs +22 -1
  7. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/utils/api.rs +445 -92
  8. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_scan_wait_terminal_state.rs +38 -7
  9. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/common/mod.rs +18 -0
  10. corgea_cli-1.14.0/tests/cloud_commands_e2e/gateway_retry.rs +222 -0
  11. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/main.rs +1 -0
  12. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/common/mod.rs +1 -0
  13. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.codex/hooks.json +0 -0
  14. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.github/workflows/npm-publish.yml +0 -0
  15. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.github/workflows/release-binaries.yml +0 -0
  16. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.github/workflows/release.yml +0 -0
  17. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.github/workflows/staging-contract.yml +0 -0
  18. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.github/workflows/test.yml +0 -0
  19. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/.gitignore +0 -0
  20. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/AGENTS.md +0 -0
  21. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/LICENSE +0 -0
  22. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/RELEASING.md +0 -0
  23. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/bin/corgea.js +0 -0
  24. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/build_release.sh +0 -0
  25. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/examples/deps_skill.rs +0 -0
  26. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/harness +0 -0
  27. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/package.json +0 -0
  28. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/pyproject.toml +0 -0
  29. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/scripts/check-linux-binary.sh +0 -0
  30. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/scripts/npm/bundle-binaries.js +0 -0
  31. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/skills/corgea/SKILL.md +0 -0
  32. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/advisories.rs +0 -0
  33. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/authorize.rs +0 -0
  34. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/cicd.rs +0 -0
  35. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/config.rs +0 -0
  36. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/catalog.rs +0 -0
  37. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/detect.rs +0 -0
  38. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/diff.rs +0 -0
  39. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/ecosystems/evaluate.rs +0 -0
  40. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/ecosystems/maven.rs +0 -0
  41. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/ecosystems/mod.rs +0 -0
  42. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/ecosystems/npm.rs +0 -0
  43. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/ecosystems/pypi.rs +0 -0
  44. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/explain.rs +0 -0
  45. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/findings.rs +0 -0
  46. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/mod.rs +0 -0
  47. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/model.rs +0 -0
  48. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/parse/mod.rs +0 -0
  49. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/parse/npm_lock.rs +0 -0
  50. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/parse/python_lock.rs +0 -0
  51. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/policy.rs +0 -0
  52. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/report.rs +0 -0
  53. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/run.rs +0 -0
  54. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/skill.rs +0 -0
  55. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/common.rs +0 -0
  56. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/correctness_tests.rs +0 -0
  57. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/detect_tests.rs +0 -0
  58. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/diff_tests.rs +0 -0
  59. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/explain_tests.rs +0 -0
  60. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/findings_tests.rs +0 -0
  61. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/maven_tests.rs +0 -0
  62. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/mod.rs +0 -0
  63. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/npm_tests.rs +0 -0
  64. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/policy_tests.rs +0 -0
  65. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/pypi_tests.rs +0 -0
  66. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/report_tests.rs +0 -0
  67. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/robustness_tests.rs +0 -0
  68. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/deps/tests/slice0_tests.rs +0 -0
  69. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/images.rs +0 -0
  70. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/incremental.rs +0 -0
  71. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/inspect.rs +0 -0
  72. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/lib.rs +0 -0
  73. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/list.rs +0 -0
  74. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/log.rs +0 -0
  75. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/main.rs +0 -0
  76. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/mcp.rs +0 -0
  77. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/detect.rs +0 -0
  78. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/exec.rs +0 -0
  79. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/mod.rs +0 -0
  80. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/parse.rs +0 -0
  81. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/render.rs +0 -0
  82. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/test_support.rs +0 -0
  83. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/tree.rs +0 -0
  84. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/uv.rs +0 -0
  85. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/precheck/verdict.rs +0 -0
  86. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/fortify.rs +0 -0
  87. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/parsers/checkmarx.rs +0 -0
  88. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/parsers/coverity.rs +0 -0
  89. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/parsers/mod.rs +0 -0
  90. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/parsers/sarif.rs +0 -0
  91. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/scanners/parsers/semgrep.rs +0 -0
  92. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/setup_hooks.rs +0 -0
  93. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/skill.rs +0 -0
  94. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/skip_scan.rs +0 -0
  95. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/targets.rs +0 -0
  96. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/utils/generic.rs +0 -0
  97. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/utils/terminal.rs +0 -0
  98. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/verify_deps/mod.rs +0 -0
  99. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/verify_deps/registry.rs +0 -0
  100. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/vuln_api/mod.rs +0 -0
  101. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/vuln_api_stub/mod.rs +0 -0
  102. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/src/wait.rs +0 -0
  103. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_advisories.rs +0 -0
  104. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_bare_install.rs +0 -0
  105. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_config_errors.rs +0 -0
  106. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_deps.rs +0 -0
  107. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_deps_skill.rs +0 -0
  108. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_exec_fallback.rs +0 -0
  109. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_install.rs +0 -0
  110. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_mcp.rs +0 -0
  111. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_npm_ci.rs +0 -0
  112. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_provenance.rs +0 -0
  113. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_remediation.rs +0 -0
  114. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_scan_include_image.rs +0 -0
  115. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_scan_sbom.rs +0 -0
  116. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_tree.rs +0 -0
  117. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_uv_sync.rs +0 -0
  118. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cli_verdict.rs +0 -0
  119. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/block_on_report.rs +0 -0
  120. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/inspect.rs +0 -0
  121. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/scan_incremental.rs +0 -0
  122. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/scan_list.rs +0 -0
  123. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/scan_skip.rs +0 -0
  124. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/cloud_commands_e2e/upload_wait.rs +0 -0
  125. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/README.md +0 -0
  126. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/go-mod-smoke/go.mod +0 -0
  127. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/go-mod-smoke/go.sum +0 -0
  128. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-gradle/build.gradle +0 -0
  129. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-gradle/gradle.lockfile +0 -0
  130. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven/pom.xml +0 -0
  131. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-alias/pom.xml +0 -0
  132. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-chained/pom.xml +0 -0
  133. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-deep-chain/pom.xml +0 -0
  134. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-managed/pom.xml +0 -0
  135. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-parent/pom.xml +0 -0
  136. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-plugin-deps/pom.xml +0 -0
  137. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-profile-mgmt/pom.xml +0 -0
  138. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-props/pom.xml +0 -0
  139. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/java-maven-revision/pom.xml +0 -0
  140. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/not-xml-pom.xml +0 -0
  141. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/package-lock.json +0 -0
  142. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/package.json +0 -0
  143. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/poetry.lock +0 -0
  144. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/pyproject.toml +0 -0
  145. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/malformed/truncated-poetry.lock +0 -0
  146. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/mixed-monorepo/services/api/requirements.txt +0 -0
  147. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/mixed-monorepo/services/web/package-lock.json +0 -0
  148. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/mixed-monorepo/services/web/package.json +0 -0
  149. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-app/.corgea/deps.yml +0 -0
  150. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-app/package-lock.json +0 -0
  151. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-app/package.json +0 -0
  152. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-monorepo/package-lock.json +0 -0
  153. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-monorepo/package.json +0 -0
  154. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-monorepo/packages/a/package.json +0 -0
  155. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-monorepo/packages/b/package.json +0 -0
  156. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-pnpm/package.json +0 -0
  157. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-pnpm/pnpm-lock.yaml +0 -0
  158. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-stale/package-lock.json +0 -0
  159. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-stale/package.json +0 -0
  160. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-transitive/package-lock.json +0 -0
  161. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-transitive/package.json +0 -0
  162. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-yarn/package.json +0 -0
  163. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/node-yarn/yarn.lock +0 -0
  164. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-pip-nolock/requirements.txt +0 -0
  165. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-poetry/poetry.lock +0 -0
  166. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-poetry/pyproject.toml +0 -0
  167. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-poetry-multi/poetry.lock +0 -0
  168. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-poetry-multi/pyproject.toml +0 -0
  169. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-uv-requirements/pyproject.toml +0 -0
  170. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-uv-requirements/requirements.txt +0 -0
  171. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/python-uv-requirements/uv.lock +0 -0
  172. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/README.md +0 -0
  173. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/check_clean.json +0 -0
  174. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/check_malware.json +0 -0
  175. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/check_unknown.json +0 -0
  176. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/check_vulnerable.json +0 -0
  177. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/package_profile.json +0 -0
  178. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/fixtures/vuln_api/package_profile_not_found.json +0 -0
  179. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/harness_smoke.rs +0 -0
  180. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/list_code_quality.rs +0 -0
  181. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/list_resolution.rs +0 -0
  182. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/logging_e2e.rs +0 -0
  183. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/vuln_api_contract.rs +0 -0
  184. {corgea_cli-1.13.0 → corgea_cli-1.14.0}/tests/wait_resolution.rs +0 -0
@@ -369,7 +369,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
369
369
 
370
370
  [[package]]
371
371
  name = "corgea"
372
- version = "1.13.0"
372
+ version = "1.14.0"
373
373
  dependencies = [
374
374
  "chrono",
375
375
  "clap",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "corgea"
3
- version = "1.13.0"
3
+ version = "1.14.0"
4
4
  edition = "2021"
5
5
  readme = "README.md"
6
6
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: corgea-cli
3
- Version: 1.13.0
3
+ Version: 1.14.0
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -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
- Waiting gives up after 10 hours; override with `CORGEA_SCAN_TIMEOUT_SECONDS`.
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::retry_on_network_error("file upload", || {
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::retry_on_network_error("scan chunk upload", || {
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::retry_on_network_error("scan upload", || {
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::retry_on_network_error("git config upload", || {
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(Duration::from_secs(1));
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