Skip to content

Commit

Permalink
Redacts secrets from JUnit reports.
Browse files Browse the repository at this point in the history
  • Loading branch information
jcamiel committed Jan 22, 2025
1 parent f7732de commit 974b724
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 19 deletions.
2 changes: 2 additions & 0 deletions integration/hurl/tests_failed/assert_secret.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ hurl --secret name1=Alice `
--error-format long `
--report-html build/assert_secret/report-html `
--report-json build/assert_secret/report-json `
--report-junit build/assert_secret/report-junit/junit.xml `
tests_failed/assert_secret.hurl

$secrets = @("Alice", "Bob")

$files = @(Get-ChildItem -Filter *.html -Recurse build/assert_secret/report-html)
$files += @(Get-ChildItem -Filter *.json build/assert_secret/)
$files += @(Get-ChildItem build/assert_secret/report-junit/junit.xml)
$files += @(Get-ChildItem tests_failed/assert_secret.err.pattern)

foreach ($secret in $secrets) {
Expand Down
2 changes: 2 additions & 0 deletions integration/hurl/tests_failed/assert_secret.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ hurl --secret name1=Alice \
--error-format long \
--report-html build/assert_secret/report-html \
--report-json build/assert_secret/report-json \
--report-junit build/assert_secret/report-junit/junit.xml \
tests_failed/assert_secret.hurl

ret=$?
Expand All @@ -20,6 +21,7 @@ secrets=("Alice" "Bob")
files=$(find build/assert_secret/report-html/*.html \
build/assert_secret/report-html/**/*.html \
build/assert_secret/report-json/*.json \
build/assert_secret/report-junit/junit.xml \
tests_failed/assert_secret.err.pattern)

for secret in "${secrets[@]}"; do
Expand Down
14 changes: 9 additions & 5 deletions packages/hurl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,18 +155,18 @@ fn export_results(
// consider only secrets introduced from cli, we have to get secrets produced during execution.
// We remove identical secrets as there may be a lot of identical secrets (those that come
// from the command line for instance)
let set = runs
let secrets = runs
.iter()
.flat_map(|r| r.hurl_result.variables.secrets())
.collect::<HashSet<_>>();
let secrets = set.iter().map(|s| s.as_ref()).collect::<Vec<_>>();
let secrets = secrets.iter().map(|s| s.as_ref()).collect::<Vec<_>>();

if let Some(file) = &opts.curl_file {
create_curl_export(runs, file)?;
}
if let Some(file) = &opts.junit_file {
logger.debug(&format!("Writing JUnit report to {}", file.display()));
create_junit_report(runs, file)?;
create_junit_report(runs, file, &secrets)?;
}
if let Some(file) = &opts.tap_file {
logger.debug(&format!("Writing TAP report to {}", file.display()));
Expand Down Expand Up @@ -195,12 +195,16 @@ fn create_curl_export(runs: &[HurlRun], filename: &Path) -> Result<(), CliError>
}

/// Creates a JUnit report for this run.
fn create_junit_report(runs: &[HurlRun], filename: &Path) -> Result<(), CliError> {
fn create_junit_report(
runs: &[HurlRun],
filename: &Path,
secrets: &[&str],
) -> Result<(), CliError> {
let testcases = runs
.iter()
.map(|r| junit::Testcase::from(&r.hurl_result, &r.content, &r.filename))
.collect::<Vec<_>>();
junit::write_report(filename, &testcases)?;
junit::write_report(filename, &testcases, secrets)?;
Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion packages/hurl/src/output/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub fn write_json(
append: bool,
) -> Result<(), io::Error> {
let response_dir = None;
// Secrets are only redacted from standard error and reports. In this cas, we want to output a
// Secrets are only redacted from standard error and reports. In this case, we want to output a
// response in a structured way. We do not change the value of the response output as it may be
// used for processing, contrary to the standard error that should be used for debug/log/messages.
let secrets = [];
Expand Down
19 changes: 14 additions & 5 deletions packages/hurl/src/report/junit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ use crate::report::junit::xml::{Element, XmlDocument};
use crate::report::ReportError;

/// Creates a JUnit from a list of `testcases`.
pub fn write_report(filename: &Path, testcases: &[Testcase]) -> Result<(), ReportError> {
///
/// `secrets` strings are redacted from messages (mainly assert error messages).
pub fn write_report(
filename: &Path,
testcases: &[Testcase],
secrets: &[&str],
) -> Result<(), ReportError> {
// We ensure that parent folder is created.
if let Some(parent) = filename.parent() {
match std::fs::create_dir_all(parent) {
Expand Down Expand Up @@ -98,7 +104,7 @@ pub fn write_report(filename: &Path, testcases: &[Testcase]) -> Result<(), Repor
Element::new("testsuites")
};

let testsuite = create_testsuite(testcases);
let testsuite = create_testsuite(testcases, secrets);
root = root.add_child(testsuite);

let doc = XmlDocument::new(root);
Expand All @@ -121,7 +127,9 @@ pub fn write_report(filename: &Path, testcases: &[Testcase]) -> Result<(), Repor
}

/// Returns a testsuite as a XML object, from a list of `testcases`.
fn create_testsuite(testcases: &[Testcase]) -> Element {
///
/// `secrets` strings are redacted from the produced HTML.
fn create_testsuite(testcases: &[Testcase], secrets: &[&str]) -> Element {
let mut tests = 0;
let mut errors = 0;
let mut failures = 0;
Expand All @@ -138,7 +146,7 @@ fn create_testsuite(testcases: &[Testcase]) -> Element {
.attr("failures", &failures.to_string());

for testcase in testcases.iter() {
let child = testcase.to_xml();
let child = testcase.to_xml(secrets);
element = element.add_child(child);
}
element
Expand All @@ -162,6 +170,7 @@ mod tests {
let content = "GET http://localhost:8000/not_found\n\
HTTP/1.0 200";
let filename = Input::new("test.hurl");
let secrets = [];
let mut testcases = vec![];
let res = HurlResult {
duration: Duration::from_millis(124),
Expand Down Expand Up @@ -212,7 +221,7 @@ mod tests {
let tc = Testcase::from(&res, content, &filename);
testcases.push(tc);

let suite = create_testsuite(&testcases);
let suite = create_testsuite(&testcases, &secrets);
let doc = XmlDocument::new(suite);
assert_eq!(
doc.to_string().unwrap(),
Expand Down
22 changes: 14 additions & 8 deletions packages/hurl/src/report/junit/testcase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use hurl_core::input::Input;

use crate::report::junit::xml::Element;
use crate::runner::HurlResult;
use crate::util::redacted::Redact;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Testcase {
Expand Down Expand Up @@ -62,7 +63,7 @@ impl Testcase {
}

/// Serializes this testcase to XML.
pub fn to_xml(&self) -> Element {
pub fn to_xml(&self, secrets: &[&str]) -> Element {
let time_in_seconds = format!("{:.3}", self.time_in_ms as f64 / 1000.0);

let mut element = Element::new("testcase")
Expand All @@ -71,11 +72,13 @@ impl Testcase {
.attr("time", &time_in_seconds);

for failure in self.failures.iter() {
element = element.add_child(Element::new("failure").text(failure));
let failure = failure.redact(secrets);
element = element.add_child(Element::new("failure").text(&failure));
}

for error in self.errors.iter() {
element = element.add_child(Element::new("error").text(error));
let error = error.redact(secrets);
element = element.add_child(Element::new("error").text(&error));
}
element
}
Expand Down Expand Up @@ -111,8 +114,9 @@ mod test {
};

let content = "";
let secrets = [];
let filename = Input::new("test.hurl");
let element = Testcase::from(&hurl_result, content, &filename).to_xml();
let element = Testcase::from(&hurl_result, content, &filename).to_xml(&secrets);
let doc = XmlDocument::new(element);
assert_eq!(
doc.to_string().unwrap(),
Expand All @@ -126,6 +130,7 @@ mod test {
HTTP/1.0 200
"#;
let filename = Input::new("test.hurl");
let secrets = [];
let hurl_result = HurlResult {
entries: vec![EntryResult {
entry_index: 1,
Expand All @@ -144,7 +149,7 @@ HTTP/1.0 200
..Default::default()
};

let element = Testcase::from(&hurl_result, content, &filename).to_xml();
let element = Testcase::from(&hurl_result, content, &filename).to_xml(&secrets);
let doc = XmlDocument::new(element);
assert_eq!(
doc.to_string().unwrap(),
Expand All @@ -162,6 +167,7 @@ HTTP/1.0 200
fn test_create_testcase_error() {
let content = "GET http://unknown";
let filename = Input::new("test.hurl");
let secrets = ["unknown"];
let hurl_result = HurlResult {
entries: vec![EntryResult {
entry_index: 1,
Expand All @@ -180,15 +186,15 @@ HTTP/1.0 200
success: false,
..Default::default()
};
let element = Testcase::from(&hurl_result, content, &filename).to_xml();
let element = Testcase::from(&hurl_result, content, &filename).to_xml(&secrets);
let doc = XmlDocument::new(element);
assert_eq!(
doc.to_string().unwrap(),
r#"<?xml version="1.0" encoding="UTF-8"?><testcase id="test.hurl" name="test.hurl" time="0.230"><error>HTTP connection
--&gt; test.hurl:1:5
|
1 | GET http://unknown
| ^^^^^^^^^^^^^^ (6) Could not resolve host: unknown
1 | GET http://***
| ^^^^^^^^^^^^^^ (6) Could not resolve host: ***
|</error></testcase>"#
);
}
Expand Down

0 comments on commit 974b724

Please sign in to comment.