Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ uuid = { version = "1.7.0", features = ["v4"] }
which = "6.0.0"
zip = "2.3.0"
tempfile = "3.12.0"
quick-xml = "0.36.1"
quick-xml = "0.41"
ignore = "0.4"
globset = "0.4"
termcolor = "1.1"
Expand Down
58 changes: 56 additions & 2 deletions src/scanners/fortify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::scan::upload_scan;
use crate::Config;
use quick_xml::events::Event;
use quick_xml::reader::Reader;
use quick_xml::XmlVersion;
use std::fs::File;
use std::io;
use std::io::{BufReader, Read};
Expand Down Expand Up @@ -92,7 +93,9 @@ fn extract_file_path(scan_file: PathBuf) -> (String, Vec<String>) {
Ok(attr) => {
let attr_key = attr.key.as_ref();
if attr_key == b"path" {
if let Ok(value) = attr.unescape_value() {
if let Ok(value) =
attr.normalized_value(XmlVersion::Implicit1_0)
{
let path_str = value.to_string();
if !paths.contains(&path_str) {
paths.push(path_str);
Expand All @@ -115,7 +118,9 @@ fn extract_file_path(scan_file: PathBuf) -> (String, Vec<String>) {
Ok(attr) => {
let attr_key = attr.key.as_ref();
if attr_key == b"path" {
if let Ok(value) = attr.unescape_value() {
if let Ok(value) =
attr.normalized_value(XmlVersion::Implicit1_0)
{
let path_str = value.to_string();
if !paths.contains(&path_str) {
paths.push(path_str);
Expand Down Expand Up @@ -145,3 +150,52 @@ fn extract_file_path(scan_file: PathBuf) -> (String, Vec<String>) {

(contents, paths)
}

#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;

/// Exercises the FVDL path extraction end to end on a representative
/// report: both the `Start` and `Empty` `SourceLocation` arms, XML entity
/// unescaping, in-`Vulnerability` scoping, and de-duplication. `fortify.rs`
/// had no coverage; this pins the behavior across the quick-xml 0.36 -> 0.41
/// bump (the migration replaced the deprecated `unescape_value` with
/// `normalized_value(XmlVersion::Implicit1_0)`, which must still resolve
/// `&amp;` -> `&` for source paths).
#[test]
fn extract_file_path_pulls_scoped_source_locations() {
let fvdl = r#"<?xml version="1.0" encoding="UTF-8"?>
<FVDL>
<Vulnerabilities>
<Vulnerability>
<SourceLocation path="src/start/a&amp;b.java"></SourceLocation>
<SourceLocation path="src/empty/App.java" line="42"/>
</Vulnerability>
<Vulnerability>
<SourceLocation path="src/empty/App.java" line="99"/>
</Vulnerability>
</Vulnerabilities>
<SourceLocation path="outside/ignored.java" line="1"/>
</FVDL>"#;

let mut tmp = tempfile::NamedTempFile::new().expect("create temp fvdl");
tmp.write_all(fvdl.as_bytes()).expect("write fvdl");
tmp.flush().expect("flush fvdl");

let (contents, paths) = extract_file_path(tmp.path().to_path_buf());

assert_eq!(contents, fvdl, "returns the raw scan contents unchanged");
assert_eq!(
paths,
vec![
// Start arm, with `&amp;` unescaped by normalized_value.
"src/start/a&b.java".to_string(),
// Empty arm, de-duplicated across the two Vulnerability blocks.
"src/empty/App.java".to_string(),
],
"extracts in-Vulnerability SourceLocation paths, unescapes entities, \
ignores the out-of-scope SourceLocation, and de-duplicates"
);
}
}
5 changes: 2 additions & 3 deletions src/verify_deps/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,11 +532,10 @@ fn split_pep440_prerelease(v: &str) -> Option<(&str, Option<String>)> {
(1, "a", r)
} else if let Some(r) = suffix.strip_prefix('b') {
(2, "b", r)
} else if let Some(r) = suffix.strip_prefix('c') {
} else {
// PEP 440 spells release-candidate `c` and `rc` interchangeably.
let r = suffix.strip_prefix('c')?;
(3, "rc", r)
} else {
return None;
};
let num_str = rest.trim_start_matches(['.', '-', '_']);
// Reject anything we didn't fully consume (combined `a1.dev2`, local
Expand Down
Loading