corgea-cli 1.14.1__tar.gz → 1.14.2__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 (186) hide show
  1. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/Cargo.lock +1 -1
  2. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/Cargo.toml +1 -1
  3. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/PKG-INFO +1 -1
  4. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/main.rs +1 -0
  5. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scan.rs +150 -18
  6. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/fortify.rs +69 -39
  7. corgea_cli-1.14.2/src/scanners/report_paths.rs +632 -0
  8. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/main.rs +1 -0
  9. corgea_cli-1.14.2/tests/cloud_commands_e2e/upload_report_paths.rs +168 -0
  10. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.codex/hooks.json +0 -0
  11. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.github/workflows/npm-publish.yml +0 -0
  12. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.github/workflows/release-binaries.yml +0 -0
  13. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.github/workflows/release.yml +0 -0
  14. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.github/workflows/staging-contract.yml +0 -0
  15. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.github/workflows/test.yml +0 -0
  16. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.gitignore +0 -0
  17. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/AGENTS.md +0 -0
  18. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/LICENSE +0 -0
  19. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/README.md +0 -0
  20. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/RELEASING.md +0 -0
  21. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/bin/corgea.js +0 -0
  22. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/build_release.sh +0 -0
  23. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/examples/deps_skill.rs +0 -0
  24. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/harness +0 -0
  25. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/package.json +0 -0
  26. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/pyproject.toml +0 -0
  27. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/scripts/check-linux-binary.sh +0 -0
  28. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/scripts/npm/bundle-binaries.js +0 -0
  29. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/skills/corgea/SKILL.md +0 -0
  30. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/advisories.rs +0 -0
  31. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/authorize.rs +0 -0
  32. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/cicd.rs +0 -0
  33. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/config.rs +0 -0
  34. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/catalog.rs +0 -0
  35. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/detect.rs +0 -0
  36. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/diff.rs +0 -0
  37. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/ecosystems/evaluate.rs +0 -0
  38. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/ecosystems/maven.rs +0 -0
  39. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/ecosystems/mod.rs +0 -0
  40. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/ecosystems/npm.rs +0 -0
  41. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/ecosystems/pypi.rs +0 -0
  42. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/explain.rs +0 -0
  43. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/findings.rs +0 -0
  44. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/mod.rs +0 -0
  45. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/model.rs +0 -0
  46. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/parse/mod.rs +0 -0
  47. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/parse/npm_lock.rs +0 -0
  48. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/parse/python_lock.rs +0 -0
  49. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/policy.rs +0 -0
  50. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/report.rs +0 -0
  51. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/run.rs +0 -0
  52. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/skill.rs +0 -0
  53. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/common.rs +0 -0
  54. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/correctness_tests.rs +0 -0
  55. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/detect_tests.rs +0 -0
  56. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/diff_tests.rs +0 -0
  57. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/explain_tests.rs +0 -0
  58. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/findings_tests.rs +0 -0
  59. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/maven_tests.rs +0 -0
  60. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/mod.rs +0 -0
  61. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/npm_tests.rs +0 -0
  62. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/policy_tests.rs +0 -0
  63. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/pypi_tests.rs +0 -0
  64. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/report_tests.rs +0 -0
  65. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/robustness_tests.rs +0 -0
  66. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/slice0_tests.rs +0 -0
  67. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/images.rs +0 -0
  68. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/incremental.rs +0 -0
  69. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/inspect.rs +0 -0
  70. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/lib.rs +0 -0
  71. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/list.rs +0 -0
  72. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/log.rs +0 -0
  73. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/mcp.rs +0 -0
  74. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/detect.rs +0 -0
  75. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/exec.rs +0 -0
  76. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/mod.rs +0 -0
  77. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/parse.rs +0 -0
  78. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/render.rs +0 -0
  79. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/test_support.rs +0 -0
  80. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/tree.rs +0 -0
  81. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/uv.rs +0 -0
  82. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/verdict.rs +0 -0
  83. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/blast.rs +0 -0
  84. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/parsers/checkmarx.rs +0 -0
  85. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/parsers/coverity.rs +0 -0
  86. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/parsers/mod.rs +0 -0
  87. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/parsers/sarif.rs +0 -0
  88. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/parsers/semgrep.rs +0 -0
  89. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/setup_hooks.rs +0 -0
  90. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/skill.rs +0 -0
  91. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/skip_scan.rs +0 -0
  92. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/targets.rs +0 -0
  93. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/utils/api.rs +0 -0
  94. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/utils/generic.rs +0 -0
  95. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/utils/terminal.rs +0 -0
  96. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/verify_deps/mod.rs +0 -0
  97. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/verify_deps/registry.rs +0 -0
  98. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/vuln_api/mod.rs +0 -0
  99. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/vuln_api_stub/mod.rs +0 -0
  100. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/wait.rs +0 -0
  101. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_advisories.rs +0 -0
  102. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_bare_install.rs +0 -0
  103. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_config_errors.rs +0 -0
  104. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_deps.rs +0 -0
  105. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_deps_skill.rs +0 -0
  106. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_exec_fallback.rs +0 -0
  107. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_install.rs +0 -0
  108. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_mcp.rs +0 -0
  109. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_npm_ci.rs +0 -0
  110. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_provenance.rs +0 -0
  111. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_remediation.rs +0 -0
  112. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_scan_include_image.rs +0 -0
  113. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_scan_sbom.rs +0 -0
  114. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_scan_wait_terminal_state.rs +0 -0
  115. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_tree.rs +0 -0
  116. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_uv_sync.rs +0 -0
  117. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_verdict.rs +0 -0
  118. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/block_on_report.rs +0 -0
  119. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/common/mod.rs +0 -0
  120. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/inspect.rs +0 -0
  121. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/scan_incremental.rs +0 -0
  122. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/scan_list.rs +0 -0
  123. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/scan_skip.rs +0 -0
  124. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/transient_retry.rs +0 -0
  125. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/upload_wait.rs +0 -0
  126. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/common/mod.rs +0 -0
  127. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/README.md +0 -0
  128. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/go-mod-smoke/go.mod +0 -0
  129. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/go-mod-smoke/go.sum +0 -0
  130. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-gradle/build.gradle +0 -0
  131. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-gradle/gradle.lockfile +0 -0
  132. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven/pom.xml +0 -0
  133. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-alias/pom.xml +0 -0
  134. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-chained/pom.xml +0 -0
  135. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-deep-chain/pom.xml +0 -0
  136. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-managed/pom.xml +0 -0
  137. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-parent/pom.xml +0 -0
  138. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-plugin-deps/pom.xml +0 -0
  139. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-profile-mgmt/pom.xml +0 -0
  140. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-props/pom.xml +0 -0
  141. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-revision/pom.xml +0 -0
  142. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/not-xml-pom.xml +0 -0
  143. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/package-lock.json +0 -0
  144. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/package.json +0 -0
  145. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/poetry.lock +0 -0
  146. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/pyproject.toml +0 -0
  147. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/truncated-poetry.lock +0 -0
  148. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/mixed-monorepo/services/api/requirements.txt +0 -0
  149. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/mixed-monorepo/services/web/package-lock.json +0 -0
  150. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/mixed-monorepo/services/web/package.json +0 -0
  151. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-app/.corgea/deps.yml +0 -0
  152. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-app/package-lock.json +0 -0
  153. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-app/package.json +0 -0
  154. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-monorepo/package-lock.json +0 -0
  155. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-monorepo/package.json +0 -0
  156. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-monorepo/packages/a/package.json +0 -0
  157. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-monorepo/packages/b/package.json +0 -0
  158. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-pnpm/package.json +0 -0
  159. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-pnpm/pnpm-lock.yaml +0 -0
  160. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-stale/package-lock.json +0 -0
  161. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-stale/package.json +0 -0
  162. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-transitive/package-lock.json +0 -0
  163. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-transitive/package.json +0 -0
  164. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-yarn/package.json +0 -0
  165. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-yarn/yarn.lock +0 -0
  166. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-pip-nolock/requirements.txt +0 -0
  167. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-poetry/poetry.lock +0 -0
  168. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-poetry/pyproject.toml +0 -0
  169. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-poetry-multi/poetry.lock +0 -0
  170. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-poetry-multi/pyproject.toml +0 -0
  171. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-uv-requirements/pyproject.toml +0 -0
  172. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-uv-requirements/requirements.txt +0 -0
  173. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-uv-requirements/uv.lock +0 -0
  174. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/README.md +0 -0
  175. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/check_clean.json +0 -0
  176. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/check_malware.json +0 -0
  177. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/check_unknown.json +0 -0
  178. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/check_vulnerable.json +0 -0
  179. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/package_profile.json +0 -0
  180. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/package_profile_not_found.json +0 -0
  181. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/harness_smoke.rs +0 -0
  182. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/list_code_quality.rs +0 -0
  183. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/list_resolution.rs +0 -0
  184. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/logging_e2e.rs +0 -0
  185. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/vuln_api_contract.rs +0 -0
  186. {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/wait_resolution.rs +0 -0
@@ -369,7 +369,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
369
369
 
370
370
  [[package]]
371
371
  name = "corgea"
372
- version = "1.14.1"
372
+ version = "1.14.2"
373
373
  dependencies = [
374
374
  "chrono",
375
375
  "clap",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "corgea"
3
- version = "1.14.1"
3
+ version = "1.14.2"
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.14.1
3
+ Version: 1.14.2
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -16,6 +16,7 @@ mod scanners {
16
16
  pub mod blast;
17
17
  pub mod fortify;
18
18
  pub mod parsers;
19
+ pub mod report_paths;
19
20
  }
20
21
  mod utils {
21
22
  pub mod api;
@@ -1,6 +1,7 @@
1
1
  use crate::cicd::*;
2
2
  use crate::log::debug;
3
3
  use crate::scanners::parsers::ScanParserFactory;
4
+ use crate::scanners::report_paths;
4
5
  use crate::{utils, Config};
5
6
  use reqwest::header;
6
7
  use reqwest::Method;
@@ -58,14 +59,46 @@ fn find_corgea_policy_files(root: &Path) -> Vec<String> {
58
59
  found
59
60
  }
60
61
 
61
- fn merge_corgea_policy_files(mut paths: Vec<String>, root: &Path) -> Vec<String> {
62
+ /// One file to send to `code-upload`.
63
+ struct SourceUpload {
64
+ /// Sent as `path=`, exactly as the report wrote it. The engine matches the
65
+ /// report's paths against this, so it stays as written even when the file
66
+ /// itself was found somewhere else.
67
+ report_path: String,
68
+ /// Where the file actually is on this machine.
69
+ local_path: PathBuf,
70
+ }
71
+
72
+ /// Pair every path a report names with the file to read for it, and append the
73
+ /// repo's `corgea.yaml` policy files.
74
+ ///
75
+ /// The prefix search runs on the report's paths alone, before the policy files
76
+ /// are added: those are found by walking `root`, so they always resolve, and
77
+ /// one of them counting as a path that "already resolves" would call off the
78
+ /// search for a report that matches nothing.
79
+ fn plan_source_uploads(root: &Path, report_paths: &[String]) -> Vec<SourceUpload> {
80
+ let prefix = report_paths::find_report_path_prefix(root, report_paths);
81
+
82
+ let mut uploads: Vec<SourceUpload> = report_paths
83
+ .iter()
84
+ .map(|path| SourceUpload {
85
+ report_path: path.clone(),
86
+ local_path: report_paths::local_report_path(root, path, &prefix),
87
+ })
88
+ .collect();
89
+
62
90
  for yaml in find_corgea_policy_files(root) {
63
- if !paths.iter().any(|path| path == &yaml) {
64
- debug(&format!("Including repo policy file: {yaml}"));
65
- paths.push(yaml);
91
+ if uploads.iter().any(|upload| upload.report_path == yaml) {
92
+ continue;
66
93
  }
94
+ debug(&format!("Including repo policy file: {yaml}"));
95
+ uploads.push(SourceUpload {
96
+ local_path: root.join(&yaml),
97
+ report_path: yaml,
98
+ });
67
99
  }
68
- paths
100
+
101
+ uploads
69
102
  }
70
103
 
71
104
  pub fn run_command(base_cmd: &String, mut command: Command) -> String {
@@ -281,7 +314,7 @@ pub fn upload_scan(
281
314
  project_name: Option<String>,
282
315
  ) -> Option<ScanUploadResult> {
283
316
  let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
284
- let paths = merge_corgea_policy_files(paths, &cwd);
317
+ let uploads = plan_source_uploads(&cwd, &paths);
285
318
  let in_ci = running_in_ci();
286
319
  let ci_platform = which_ci();
287
320
  let github_env_vars = get_github_env_vars();
@@ -331,11 +364,23 @@ pub fn upload_scan(
331
364
  let mut upload_error_count = 0;
332
365
  let mut platform_declined = false;
333
366
 
334
- 'files: for path in &paths {
335
- if !Path::new(&path).exists() {
367
+ 'files: for upload in &uploads {
368
+ let path = &upload.report_path;
369
+ let fp = upload.local_path.as_path();
370
+
371
+ if !fp.exists() {
372
+ // Name where the file was looked for whenever that is not the path
373
+ // in the report, so a prefix that resolved most of the report but
374
+ // not this path is visible rather than mystifying.
375
+ let looked_in = if fp.as_os_str() == path.as_str() {
376
+ String::new()
377
+ } else {
378
+ format!(" (looked for it at '{}')", fp.display())
379
+ };
336
380
  log::error!(
337
- "Required file {} not found which is required for the scan, exiting.",
338
- path
381
+ "Required file {}{} not found which is required for the scan, exiting.",
382
+ path,
383
+ looked_in
339
384
  );
340
385
  std::process::exit(1);
341
386
  }
@@ -349,7 +394,6 @@ pub fn upload_scan(
349
394
  base_url, api_base, run_id, path
350
395
  );
351
396
  debug(&format!("Uploading file: {}", path));
352
- let fp = Path::new(&path);
353
397
 
354
398
  let mut attempts = 0;
355
399
  let mut success = false;
@@ -419,7 +463,7 @@ pub fn upload_scan(
419
463
  // Everything the aborted walk never attempted still counts as unsent, or
420
464
  // the closing summary would report one failure for a whole skipped tree.
421
465
  if platform_declined {
422
- let distinct: HashSet<&String> = paths.iter().collect();
466
+ let distinct: HashSet<&String> = uploads.iter().map(|upload| &upload.report_path).collect();
423
467
  let unsent = distinct.len() - uploaded_paths.len();
424
468
  upload_error_count += unsent;
425
469
  log::warn!(
@@ -788,21 +832,109 @@ mod tests {
788
832
  );
789
833
  }
790
834
 
835
+ fn planned(root: &Path, report_paths: &[&str]) -> Vec<(String, String)> {
836
+ plan_source_uploads(
837
+ root,
838
+ &report_paths
839
+ .iter()
840
+ .map(|path| path.to_string())
841
+ .collect::<Vec<_>>(),
842
+ )
843
+ .into_iter()
844
+ .map(|upload| {
845
+ (
846
+ upload.report_path,
847
+ upload.local_path.to_string_lossy().into_owned(),
848
+ )
849
+ })
850
+ .collect()
851
+ }
852
+
791
853
  #[test]
792
- fn merge_corgea_policy_files_appends_missing_and_skips_duplicates() {
854
+ fn plan_source_uploads_appends_policy_files_and_skips_duplicates() {
793
855
  let root = tempfile::tempdir().unwrap();
794
856
  write_policy(&root.path().join("corgea.yaml"));
857
+ let policy = root
858
+ .path()
859
+ .join("corgea.yaml")
860
+ .to_string_lossy()
861
+ .into_owned();
795
862
 
796
863
  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()]
864
+ planned(root.path(), &["src/source.py"]),
865
+ vec![
866
+ ("src/source.py".to_string(), "src/source.py".to_string()),
867
+ ("corgea.yaml".to_string(), policy.clone()),
868
+ ]
869
+ );
870
+ assert_eq!(
871
+ planned(root.path(), &["src/source.py", "corgea.yaml"]),
872
+ vec![
873
+ ("src/source.py".to_string(), "src/source.py".to_string()),
874
+ ("corgea.yaml".to_string(), "corgea.yaml".to_string()),
875
+ ]
876
+ );
877
+ }
878
+
879
+ /// Only the file read from disk is rebased. `path=` keeps the report's own
880
+ /// path, because that is what the engine matches the report against.
881
+ #[test]
882
+ fn plan_source_uploads_rebases_the_local_file_but_not_the_uploaded_path() {
883
+ let root = tempfile::tempdir().unwrap();
884
+ for path in ["src/a.py", "src/b.py"] {
885
+ std::fs::create_dir_all(root.path().join(path).parent().unwrap()).unwrap();
886
+ std::fs::write(root.path().join(path), "x = 1\n").unwrap();
887
+ }
888
+
889
+ assert_eq!(
890
+ planned(
891
+ root.path(),
892
+ &["/builds/acme/repo/src/a.py", "/builds/acme/repo/src/b.py"]
893
+ ),
894
+ vec![
895
+ (
896
+ "/builds/acme/repo/src/a.py".to_string(),
897
+ root.path().join("src/a.py").to_string_lossy().into_owned(),
898
+ ),
899
+ (
900
+ "/builds/acme/repo/src/b.py".to_string(),
901
+ root.path().join("src/b.py").to_string_lossy().into_owned(),
902
+ ),
903
+ ]
904
+ );
905
+ }
906
+
907
+ /// A policy file found by walking the root always exists, so letting one
908
+ /// into the prefix search would report the whole report as already
909
+ /// matching and call the search off.
910
+ #[test]
911
+ fn plan_source_uploads_settles_the_prefix_before_adding_policy_files() {
912
+ let root = tempfile::tempdir().unwrap();
913
+ write_policy(&root.path().join("corgea.yaml"));
914
+ for path in ["src/a.py", "src/b.py"] {
915
+ std::fs::create_dir_all(root.path().join(path).parent().unwrap()).unwrap();
916
+ std::fs::write(root.path().join(path), "x = 1\n").unwrap();
917
+ }
918
+
919
+ let plan = planned(root.path(), &["ci/src/a.py", "ci/src/b.py"]);
920
+
921
+ assert_eq!(
922
+ plan[0],
923
+ (
924
+ "ci/src/a.py".to_string(),
925
+ root.path().join("src/a.py").to_string_lossy().into_owned(),
926
+ )
799
927
  );
800
928
  assert_eq!(
801
- merge_corgea_policy_files(
802
- vec!["src/source.py".into(), "corgea.yaml".into()],
929
+ plan[2],
930
+ (
931
+ "corgea.yaml".to_string(),
803
932
  root.path()
933
+ .join("corgea.yaml")
934
+ .to_string_lossy()
935
+ .into_owned(),
804
936
  ),
805
- vec!["src/source.py".to_string(), "corgea.yaml".to_string()]
937
+ "the policy file is read from the root, never through the report's prefix"
806
938
  );
807
939
  }
808
940
  }
@@ -1,8 +1,9 @@
1
1
  use crate::scan::{upload_scan, ScanUploadResult};
2
2
  use crate::Config;
3
- use quick_xml::events::Event;
3
+ use quick_xml::events::{BytesStart, Event};
4
4
  use quick_xml::reader::Reader;
5
5
  use quick_xml::XmlVersion;
6
+ use std::collections::HashSet;
6
7
  use std::fs::File;
7
8
  use std::io;
8
9
  use std::io::{BufReader, Read};
@@ -68,6 +69,15 @@ pub fn parse(
68
69
  result
69
70
  }
70
71
 
72
+ /// The raw FVDL and the source files the engine will look up for it.
73
+ ///
74
+ /// A vulnerability can carry several `SourceLocation`s, where the earlier ones
75
+ /// are the enclosing scope and only the last is the finding itself, and the
76
+ /// engine reads that last one only. The earlier ones can sit in an unrelated
77
+ /// tree -- Fortify records its own bundled libraries under
78
+ /// `AppData/Local/Fortify/.../_fortify_libraries_/` -- which is not on the
79
+ /// machine running the CLI, so collecting them aborted the upload of a report
80
+ /// whose real files were all present.
71
81
  fn extract_file_path(scan_file: PathBuf) -> (String, Vec<String>) {
72
82
  let mut paths: Vec<String> = Vec::new();
73
83
 
@@ -84,6 +94,9 @@ fn extract_file_path(scan_file: PathBuf) -> (String, Vec<String>) {
84
94
 
85
95
  let mut buf = Vec::new();
86
96
  let mut in_vulnerability = false;
97
+ let mut seen = HashSet::new();
98
+ // The last SourceLocation seen in the current Vulnerability.
99
+ let mut selected: Option<String> = None;
87
100
 
88
101
  loop {
89
102
  match xml_reader.read_event_into(&mut buf) {
@@ -94,48 +107,15 @@ fn extract_file_path(scan_file: PathBuf) -> (String, Vec<String>) {
94
107
  if tag_name == b"Vulnerability" {
95
108
  in_vulnerability = true;
96
109
  } else if tag_name == b"SourceLocation" && in_vulnerability {
97
- for attr_result in e.attributes() {
98
- match attr_result {
99
- Ok(attr) => {
100
- let attr_key = attr.key.as_ref();
101
- if attr_key == b"path" {
102
- if let Ok(value) =
103
- attr.normalized_value(XmlVersion::Implicit1_0)
104
- {
105
- let path_str = value.to_string();
106
- if !paths.contains(&path_str) {
107
- paths.push(path_str);
108
- }
109
- }
110
- }
111
- }
112
- Err(e) => println!("Error processing attribute: {}", e),
113
- }
110
+ if let Some(path) = source_location_path(e) {
111
+ selected = Some(path);
114
112
  }
115
113
  }
116
114
  }
117
115
  Ok(Event::Empty(ref e)) => {
118
- let e_name = e.name();
119
- let tag_name = e_name.as_ref();
120
-
121
- if tag_name == b"SourceLocation" && in_vulnerability {
122
- for attr_result in e.attributes() {
123
- match attr_result {
124
- Ok(attr) => {
125
- let attr_key = attr.key.as_ref();
126
- if attr_key == b"path" {
127
- if let Ok(value) =
128
- attr.normalized_value(XmlVersion::Implicit1_0)
129
- {
130
- let path_str = value.to_string();
131
- if !paths.contains(&path_str) {
132
- paths.push(path_str);
133
- }
134
- }
135
- }
136
- }
137
- Err(e) => println!("Error processing attribute: {}", e),
138
- }
116
+ if e.name().as_ref() == b"SourceLocation" && in_vulnerability {
117
+ if let Some(path) = source_location_path(e) {
118
+ selected = Some(path);
139
119
  }
140
120
  }
141
121
  }
@@ -145,6 +125,12 @@ fn extract_file_path(scan_file: PathBuf) -> (String, Vec<String>) {
145
125
 
146
126
  if tag_name == b"Vulnerability" {
147
127
  in_vulnerability = false;
128
+
129
+ if let Some(path) = selected.take() {
130
+ if seen.insert(path.clone()) {
131
+ paths.push(path);
132
+ }
133
+ }
148
134
  }
149
135
  }
150
136
  Ok(Event::Eof) => break,
@@ -157,6 +143,23 @@ fn extract_file_path(scan_file: PathBuf) -> (String, Vec<String>) {
157
143
  (contents, paths)
158
144
  }
159
145
 
146
+ /// The `path` attribute of a `SourceLocation`, with XML entities resolved.
147
+ fn source_location_path(element: &BytesStart) -> Option<String> {
148
+ for attr_result in element.attributes() {
149
+ match attr_result {
150
+ Ok(attr) if attr.key.as_ref() == b"path" => {
151
+ if let Ok(value) = attr.normalized_value(XmlVersion::Implicit1_0) {
152
+ return Some(value.to_string());
153
+ }
154
+ }
155
+ Ok(_) => {}
156
+ Err(e) => println!("Error processing attribute: {}", e),
157
+ }
158
+ }
159
+
160
+ None
161
+ }
162
+
160
163
  #[cfg(test)]
161
164
  mod tests {
162
165
  use super::*;
@@ -176,6 +179,8 @@ mod tests {
176
179
  <Vulnerabilities>
177
180
  <Vulnerability>
178
181
  <SourceLocation path="src/start/a&amp;b.java"></SourceLocation>
182
+ </Vulnerability>
183
+ <Vulnerability>
179
184
  <SourceLocation path="src/empty/App.java" line="42"/>
180
185
  </Vulnerability>
181
186
  <Vulnerability>
@@ -204,4 +209,29 @@ mod tests {
204
209
  ignores the out-of-scope SourceLocation, and de-duplicates"
205
210
  );
206
211
  }
212
+
213
+ /// The engine reads the last SourceLocation of a vulnerability and no
214
+ /// other, so the earlier ones must not be uploaded: they can name Fortify's
215
+ /// own bundled libraries, which are not on the machine running the CLI and
216
+ /// used to abort the upload before any file was sent.
217
+ #[test]
218
+ fn extract_file_path_keeps_only_the_location_the_finding_is_reported_at() {
219
+ let fvdl = r#"<?xml version="1.0" encoding="UTF-8"?>
220
+ <FVDL>
221
+ <Vulnerabilities>
222
+ <Vulnerability>
223
+ <SourceLocation path="C:/Users/dev/AppData/Local/Fortify/_fortify_libraries_/node/fs.d.ts" line="1"/>
224
+ <SourceLocation path="src/App.java" line="42"/>
225
+ </Vulnerability>
226
+ </Vulnerabilities>
227
+ </FVDL>"#;
228
+
229
+ let mut tmp = tempfile::NamedTempFile::new().expect("create temp fvdl");
230
+ tmp.write_all(fvdl.as_bytes()).expect("write fvdl");
231
+ tmp.flush().expect("flush fvdl");
232
+
233
+ let (_, paths) = extract_file_path(tmp.path().to_path_buf());
234
+
235
+ assert_eq!(paths, vec!["src/App.java".to_string()]);
236
+ }
207
237
  }