Skip to content

Commit

Permalink
encode percent signs (#16)
Browse files Browse the repository at this point in the history
* encode percent signs

* use same test as package-url/purl-spec#273
  • Loading branch information
matt-phylum authored Nov 22, 2023
1 parent c71730c commit 3d21fd1
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 4 deletions.
9 changes: 5 additions & 4 deletions purl/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ const PATH: &AsciiSet = &QUERY.add(b'?').add(b'`').add(b'{').add(b'}');

// https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst#how-to-build-purl-string-from-its-components
// We mostly use the standard URL rules, but the PURL spec says '@' '?' '#' must
// be escaped except when used as a separator.
const PURL_PATH: &AsciiSet = &PATH.add(b'@').add(b'?').add(b'#');
// be escaped except when used as a separator, and we do all the encoding in one
// pass so we need to include '%'.
const PURL_PATH: &AsciiSet = &PATH.add(b'@').add(b'?').add(b'#').add(b'%');
const PURL_PATH_SEGMENT: &AsciiSet = &PURL_PATH.add(b'/');
// For compatibility with PURL implementations that treat qualifiers as
// form-urlencoded, escape '+' as well.
const PURL_QUERY: &AsciiSet = &QUERY.add(b'@').add(b'?').add(b'#').add(b'+');
const PURL_FRAGMENT: &AsciiSet = &FRAGMENT.add(b'@').add(b'?').add(b'#');
const PURL_QUERY: &AsciiSet = &QUERY.add(b'@').add(b'?').add(b'#').add(b'+').add(b'%');
const PURL_FRAGMENT: &AsciiSet = &FRAGMENT.add(b'@').add(b'?').add(b'#').add(b'%');

impl<T> fmt::Display for GenericPurl<T>
where
Expand Down
36 changes: 36 additions & 0 deletions purl_test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1260,3 +1260,39 @@ fn plus_signs_and_spaces() {
"Incorrect string representation"
);
}
#[test]
/// unsupported: percent signs are properly encoded and decoded
fn unsupported_percent_signs_are_properly_encoded_and_decoded() {
assert!(
matches!(Purl::from_str("pkg:generic/100%25/100%25@100%25?repository_url=https://example.com/100%2525/#100%25"),
Err(PackageError::UnsupportedType)), "Type {} is not supported", "generic"
);
let parsed = match GenericPurl::<String>::from_str(
"pkg:generic/100%25/100%25@100%25?repository_url=https://example.com/100%2525/#100%25",
) {
Ok(purl) => purl,
Err(error) => {
panic!(
"Failed to parse valid purl {:?}: {}",
"pkg:generic/100%25/100%25@100%25?repository_url=https://example.com/100%2525/#100%25",
error
)
},
};
assert_eq!("generic", parsed.package_type(), "Incorrect package type");
assert_eq!(Some("100%"), parsed.namespace(), "Incorrect namespace");
assert_eq!("100%", parsed.name(), "Incorrect name");
assert_eq!(Some("100%"), parsed.version(), "Incorrect version");
assert_eq!(Some("100%"), parsed.subpath(), "Incorrect subpath");
let expected_qualifiers: HashMap<&str, &str> =
[("repository_url", "https://example.com/100%25/")].into_iter().collect();
assert_eq!(
expected_qualifiers,
parsed.qualifiers().iter().map(|(k, v)| (k.as_str(), v)).collect::<HashMap<&str, &str>>()
);
assert_eq!(
"pkg:generic/100%25/100%25@100%25?repository_url=https://example.com/100%2525/#100%25",
&parsed.to_string(),
"Incorrect string representation"
);
}
14 changes: 14 additions & 0 deletions xtask/src/generate_tests/phylum-test-suite-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,19 @@
},
"subpath": null,
"is_invalid": false
},
{
"description": "percent signs are properly encoded and decoded",
"purl": "pkg:generic/100%25/100%25@100%25?repository_url=https://example.com/100%2525/#100%25",
"canonical_purl": "pkg:generic/100%25/100%25@100%25?repository_url=https://example.com/100%2525/#100%25",
"type": "generic",
"namespace": "100%",
"name": "100%",
"version": "100%",
"qualifiers": {
"repository_url": "https://example.com/100%25/"
},
"subpath": "100%",
"is_invalid": false
}
]

0 comments on commit 3d21fd1

Please sign in to comment.