Skip to content
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ For more details, see our [documentation](https://cve-bin-tool.readthedocs.io/en
- [Scanning an SBOM file for known vulnerabilities](#scanning-an-sbom-file-for-known-vulnerabilities)
- [Generating an SBOM](#generating-an-sbom)
- [Generating a VEX](#generating-a-vex)
- [Archiving VEX Files](#archiving-vex-files)
- [Triaging vulnerabilities](#triaging-vulnerabilities)
- [Using the tool offline](#using-the-tool-offline)
- [Using CVE Binary Tool in GitHub Actions](#using-cve-binary-tool-in-github-actions)
Expand Down Expand Up @@ -134,6 +135,28 @@ Valid VEX types are [CSAF](https://oasis-open.github.io/csaf-documentation/), [C

The [VEX generation how-to guide](https://github.com/intel/cve-bin-tool/blob/main/doc/how_to_guides/vex_generation.md) provides additional VEX generation examples.

### Archiving VEX Files

When software components are updated or removed, VEX files may contain obsolete vulnerability triage information. The VEX archive feature helps maintain clean and current VEX files:

```bash
cve-bin-tool vex-archive --vex <vex-file> --report <scan-report>
```

This command:
- Identifies VEX entries for components no longer present in your scan
- Archives obsolete vulnerability triage information to a separate file
- Creates backup files for safety
- Generates detailed operation summaries

Use `--dry-run` to preview changes without modifying files:

```bash
cve-bin-tool vex-archive --vex my-project.vex.json --report current-scan.json --dry-run
```

The [VEX archiving how-to guide](https://github.com/intel/cve-bin-tool/blob/main/doc/how_to_guides/vex_archiving.md) provides detailed usage instructions and examples.

### Triaging vulnerabilities

The `--vex-file` option can be used to add extra triage data like remarks, comments etc. while scanning a directory so that output will reflect this triage data and you can save time of re-triaging (Usage: `cve-bin-tool --vex-file test.json /path/to/scan`).
Expand Down
81 changes: 81 additions & 0 deletions cve_bin_tool/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,83 @@ def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, values)


def vex_archive_main(argv):
"""Handle the vex-archive subcommand"""
from cve_bin_tool.vex_manager.archive import VEXArchiveManager

parser = argparse.ArgumentParser(
prog="cve-bin-tool vex-archive",
description="Archive obsolete VEX entries based on new scan reports. "
"Remove or archive triage information that is no longer applicable "
"(for example, when a component is updated and the original CVE no longer applies)",
)

parser.add_argument(
"--vex",
required=True,
help="Path to the VEX file you want to clean up",
metavar="VEX_FILE_PATH",
)

parser.add_argument(
"--report",
required=True,
help="Path to a new cve-bin-tool JSON scan report",
metavar="NEW_REPORT_PATH",
)

parser.add_argument(
"--archive-file",
default="vex-archive.json",
help="File where obsolete entries will be stored (default: vex-archive.json)",
metavar="ARCHIVE_FILE_PATH",
)

parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would be archived without making any changes",
)

args = parser.parse_args(argv)

# Process VEX archiving
try:
archive_manager = VEXArchiveManager(
vex_file_path=args.vex,
report_file_path=args.report,
archive_file_path=args.archive_file,
dry_run=args.dry_run,
)

result = archive_manager.process()

if result["success"]:
if args.dry_run:
if result["obsolete_entries"] > 0:
LOGGER.info(
f"DRY RUN: Would archive {result['obsolete_entries']} obsolete entries"
)
else:
LOGGER.info(
"DRY RUN: No obsolete entries found - VEX file is up to date"
)
elif result["obsolete_entries"] > 0:
LOGGER.info(
f"Success: Archived {result['obsolete_entries']} obsolete entries to {result['archive_file']}. VEX file backup and archive summary created."
)
else:
LOGGER.info("No obsolete entries found - VEX file is up to date")
return 0
else:
LOGGER.error(f"VEX archiving failed: {result['error']}")
return 1

except Exception as e:
LOGGER.error(f"Unexpected error: {e}")
return 1


def main(argv=None):
"""Scan a binary file for certain open source libraries that may have CVEs"""
if sys.version_info < (3, 8):
Expand All @@ -105,6 +182,10 @@ def main(argv=None):
# Reset logger level to info
LOGGER.setLevel(logging.INFO)

# Check if this is a vex-archive command
if len(argv) > 1 and argv[1] == "vex-archive":
return vex_archive_main(argv[2:])

parser = argparse.ArgumentParser(
prog="cve-bin-tool",
description=textwrap.dedent(
Expand Down
10 changes: 7 additions & 3 deletions cve_bin_tool/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,9 +448,13 @@ def decode_bom_ref(ref: str):
else:
return None

if product and vendor and version:
if validate_product_vendor(product, vendor) and validate_version(version):
return ProductInfo(vendor.strip(), product.strip(), version.strip())
if product and version:
# Handle case where vendor might be None for certain BOM references
vendor_name = vendor.strip() if vendor else "unknown"
if (
vendor is None or validate_product_vendor(product, vendor)
) and validate_version(version):
return ProductInfo(vendor_name, product.strip(), version.strip(), location)

return None

Expand Down
Loading
Loading