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.
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/Cargo.lock +1 -1
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/Cargo.toml +1 -1
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/PKG-INFO +1 -1
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/main.rs +1 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scan.rs +150 -18
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/fortify.rs +69 -39
- corgea_cli-1.14.2/src/scanners/report_paths.rs +632 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/main.rs +1 -0
- corgea_cli-1.14.2/tests/cloud_commands_e2e/upload_report_paths.rs +168 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.codex/hooks.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.github/workflows/npm-publish.yml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.github/workflows/release-binaries.yml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.github/workflows/release.yml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.github/workflows/staging-contract.yml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.github/workflows/test.yml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/.gitignore +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/AGENTS.md +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/LICENSE +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/README.md +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/RELEASING.md +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/bin/corgea.js +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/build_release.sh +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/examples/deps_skill.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/harness +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/package.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/pyproject.toml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/scripts/check-linux-binary.sh +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/scripts/npm/bundle-binaries.js +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/skills/corgea/SKILL.md +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/advisories.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/authorize.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/cicd.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/config.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/catalog.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/detect.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/diff.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/ecosystems/evaluate.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/ecosystems/maven.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/ecosystems/mod.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/ecosystems/npm.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/ecosystems/pypi.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/explain.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/findings.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/mod.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/model.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/parse/mod.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/parse/npm_lock.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/parse/python_lock.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/policy.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/report.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/run.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/skill.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/common.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/correctness_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/detect_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/diff_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/explain_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/findings_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/maven_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/mod.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/npm_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/policy_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/pypi_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/report_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/robustness_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/deps/tests/slice0_tests.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/images.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/incremental.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/inspect.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/lib.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/list.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/log.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/mcp.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/detect.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/exec.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/mod.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/parse.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/render.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/test_support.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/tree.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/uv.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/precheck/verdict.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/blast.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/parsers/checkmarx.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/parsers/coverity.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/parsers/mod.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/parsers/sarif.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/scanners/parsers/semgrep.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/setup_hooks.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/skill.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/skip_scan.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/targets.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/utils/api.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/utils/generic.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/utils/terminal.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/verify_deps/mod.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/verify_deps/registry.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/vuln_api/mod.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/vuln_api_stub/mod.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/src/wait.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_advisories.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_bare_install.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_config_errors.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_deps.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_deps_skill.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_exec_fallback.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_install.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_mcp.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_npm_ci.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_provenance.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_remediation.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_scan_include_image.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_scan_sbom.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_scan_wait_terminal_state.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_tree.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_uv_sync.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cli_verdict.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/block_on_report.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/common/mod.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/inspect.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/scan_incremental.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/scan_list.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/scan_skip.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/transient_retry.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/cloud_commands_e2e/upload_wait.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/common/mod.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/README.md +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/go-mod-smoke/go.mod +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/go-mod-smoke/go.sum +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-gradle/build.gradle +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-gradle/gradle.lockfile +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven/pom.xml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-alias/pom.xml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-chained/pom.xml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-deep-chain/pom.xml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-managed/pom.xml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-parent/pom.xml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-plugin-deps/pom.xml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-profile-mgmt/pom.xml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-props/pom.xml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/java-maven-revision/pom.xml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/not-xml-pom.xml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/package-lock.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/package.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/poetry.lock +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/pyproject.toml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/malformed/truncated-poetry.lock +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/mixed-monorepo/services/api/requirements.txt +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/mixed-monorepo/services/web/package-lock.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/mixed-monorepo/services/web/package.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-app/.corgea/deps.yml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-app/package-lock.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-app/package.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-monorepo/package-lock.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-monorepo/package.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-monorepo/packages/a/package.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-monorepo/packages/b/package.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-pnpm/package.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-pnpm/pnpm-lock.yaml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-stale/package-lock.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-stale/package.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-transitive/package-lock.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-transitive/package.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-yarn/package.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/node-yarn/yarn.lock +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-pip-nolock/requirements.txt +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-poetry/poetry.lock +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-poetry/pyproject.toml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-poetry-multi/poetry.lock +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-poetry-multi/pyproject.toml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-uv-requirements/pyproject.toml +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-uv-requirements/requirements.txt +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/python-uv-requirements/uv.lock +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/README.md +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/check_clean.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/check_malware.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/check_unknown.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/check_vulnerable.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/package_profile.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/fixtures/vuln_api/package_profile_not_found.json +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/harness_smoke.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/list_code_quality.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/list_resolution.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/logging_e2e.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/vuln_api_contract.rs +0 -0
- {corgea_cli-1.14.1 → corgea_cli-1.14.2}/tests/wait_resolution.rs +0 -0
|
@@ -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
|
-
|
|
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
|
|
64
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
335
|
-
|
|
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> =
|
|
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
|
|
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
|
-
|
|
798
|
-
vec![
|
|
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
|
-
|
|
802
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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
|
-
|
|
119
|
-
|
|
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&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
|
}
|