diff --git a/resources/license_detection/index_build_policy.toml b/resources/license_detection/index_build_policy.toml index 3a05ccbed..66aae007f 100644 --- a/resources/license_detection/index_build_policy.toml +++ b/resources/license_detection/index_build_policy.toml @@ -29,6 +29,8 @@ ignored_rules = [ ignored_licenses = [] [overlay_reasons.rules] +"bsd-new_and_bsd-simplified_1.RULE" = "Require both BSD-3-Clause and BSD-2-Clause names so repeated BSD-3-Clause HTML headings cannot be consumed by a partial composite match that invents BSD-2-Clause (issue #1457)." +"elastic-license-v2_or_mongodb-sspl-1.0_5.RULE" = "Require both Elastic License 2.0 and Server Side Public License names so generic XML/HTML license markup and MPL-2.0 headings cannot claim unrelated Elastic/SSPL terms (issue #1457)." "agpl-3.0-plus_101.RULE" = "Treat bare AGPL mentions as clue-only evidence so short comparative references stay inspectable without becoming hard detections." "apache-2.0_and_lgpl-2.1_1.RULE" = "Keep the mixed Apache-2.0/LGPL-2.1 notice detectable only when both COPYING.LGPL and COPYING.ASL2 references are actually present." "apache-2.0_and_lgpl-2.1_2.RULE" = "Preserve a real typoed Apache-2.0/LGPL-2.1 notice seen in the wild without broadening generic dual-license detection." diff --git a/resources/license_detection/license_index.zst b/resources/license_detection/license_index.zst index 28b450960..4ae259ede 100644 Binary files a/resources/license_detection/license_index.zst and b/resources/license_detection/license_index.zst differ diff --git a/resources/license_detection/overlay/rules/bsd-new_and_bsd-simplified_1.RULE b/resources/license_detection/overlay/rules/bsd-new_and_bsd-simplified_1.RULE new file mode 100644 index 000000000..4d21cc03f --- /dev/null +++ b/resources/license_detection/overlay/rules/bsd-new_and_bsd-simplified_1.RULE @@ -0,0 +1,8 @@ +--- +license_expression: bsd-new AND bsd-simplified +is_license_notice: yes +--- + +The source code is derived from `FreeBSD`_ and `SCC`_. FreeBSD uses +various BSD licenses, including {{BSD-3-Clause}} and {{BSD-2-Clause}}. The SCC code +is used under the BSD-3-Clause license with the author's permission. diff --git a/resources/license_detection/overlay/rules/elastic-license-v2_or_mongodb-sspl-1.0_5.RULE b/resources/license_detection/overlay/rules/elastic-license-v2_or_mongodb-sspl-1.0_5.RULE new file mode 100644 index 000000000..c37077c2c --- /dev/null +++ b/resources/license_detection/overlay/rules/elastic-license-v2_or_mongodb-sspl-1.0_5.RULE @@ -0,0 +1,20 @@ +--- +license_expression: elastic-license-v2 OR mongodb-sspl-1.0 +is_license_tag: yes +ignorable_urls: + - https://raw.githubusercontent.com/elastic/elasticsearch/v7.16.3/licenses/ELASTIC-LICENSE-2.0.txt + - https://www.mongodb.com/licensing/server-side-public-license +--- + + + + {{Elastic License 2.0}} + https://raw.githubusercontent.com/elastic/elasticsearch/v7.16.3/licenses/ELASTIC-LICENSE-2.0.txt + repo + + + {{Server Side Public License, v 1}} + https://www.mongodb.com/licensing/server-side-public-license + repo + + diff --git a/testdata/license-golden/datadriven/external/maven-bsd-heading.html b/testdata/license-golden/datadriven/external/maven-bsd-heading.html new file mode 100644 index 000000000..668c2370f --- /dev/null +++ b/testdata/license-golden/datadriven/external/maven-bsd-heading.html @@ -0,0 +1,4 @@ +

Overview

+

Typically the licenses listed for the project are that of the project itself, and not of dependencies.

+

Project Licenses

+

BSD 3-Clause

diff --git a/testdata/license-golden/datadriven/external/maven-bsd-heading.html.yml b/testdata/license-golden/datadriven/external/maven-bsd-heading.html.yml new file mode 100644 index 000000000..50c402f42 --- /dev/null +++ b/testdata/license-golden/datadriven/external/maven-bsd-heading.html.yml @@ -0,0 +1,5 @@ +license_expressions: + - bsd-new + - bsd-new + - bsd-new +notes: Synthetic HTML license-heading regression for issue 1457. diff --git a/testdata/license-golden/datadriven/external/maven-composite-bsd.txt b/testdata/license-golden/datadriven/external/maven-composite-bsd.txt new file mode 100644 index 000000000..524edce75 --- /dev/null +++ b/testdata/license-golden/datadriven/external/maven-composite-bsd.txt @@ -0,0 +1,3 @@ +The source code is derived from `FreeBSD`_ and `SCC`_. FreeBSD uses +various BSD licenses, including BSD-3-Clause and BSD-2-Clause. The SCC code +is used under the BSD-3-Clause license with the author's permission. diff --git a/testdata/license-golden/datadriven/external/maven-composite-bsd.txt.yml b/testdata/license-golden/datadriven/external/maven-composite-bsd.txt.yml new file mode 100644 index 000000000..bfcddb9e8 --- /dev/null +++ b/testdata/license-golden/datadriven/external/maven-composite-bsd.txt.yml @@ -0,0 +1,3 @@ +license_expressions: + - bsd-new AND bsd-simplified +notes: Positive control copied from ScanCode Toolkit rule bsd-new_and_bsd-simplified_1.RULE at commit 5e8448ecf2397c6eb7e7eb50326c8c880d9b598b. License-detection data is CC-BY-4.0; copyright nexB Inc. and others. diff --git a/testdata/license-golden/datadriven/external/maven-composite-elastic.xml b/testdata/license-golden/datadriven/external/maven-composite-elastic.xml new file mode 100644 index 000000000..5b133fd5c --- /dev/null +++ b/testdata/license-golden/datadriven/external/maven-composite-elastic.xml @@ -0,0 +1,12 @@ + + + Elastic License 2.0 + https://raw.githubusercontent.com/elastic/elasticsearch/v7.16.3/licenses/ELASTIC-LICENSE-2.0.txt + repo + + + Server Side Public License, v 1 + https://www.mongodb.com/licensing/server-side-public-license + repo + + diff --git a/testdata/license-golden/datadriven/external/maven-composite-elastic.xml.yml b/testdata/license-golden/datadriven/external/maven-composite-elastic.xml.yml new file mode 100644 index 000000000..2be9e801f --- /dev/null +++ b/testdata/license-golden/datadriven/external/maven-composite-elastic.xml.yml @@ -0,0 +1,3 @@ +license_expressions: + - elastic-license-v2 OR mongodb-sspl-1.0 +notes: Positive control copied from ScanCode Toolkit rule elastic-license-v2_or_mongodb-sspl-1.0_5.RULE at commit 5e8448ecf2397c6eb7e7eb50326c8c880d9b598b. License-detection data is CC-BY-4.0; copyright nexB Inc. and others. diff --git a/testdata/license-golden/datadriven/external/maven-mpl-heading.html b/testdata/license-golden/datadriven/external/maven-mpl-heading.html new file mode 100644 index 000000000..2ed72ec72 --- /dev/null +++ b/testdata/license-golden/datadriven/external/maven-mpl-heading.html @@ -0,0 +1,5 @@ +Example Project Licenses +

Project Licenses

+

Mozilla Public License 2.0

+ + \ No newline at end of file diff --git a/testdata/license-golden/datadriven/external/maven-mpl-heading.html.yml b/testdata/license-golden/datadriven/external/maven-mpl-heading.html.yml new file mode 100644 index 000000000..238a72927 --- /dev/null +++ b/testdata/license-golden/datadriven/external/maven-mpl-heading.html.yml @@ -0,0 +1,5 @@ +license_expressions: + - mpl-2.0 + - mpl-2.0 + - mpl-2.0 +notes: Synthetic HTML license-heading regression for issue 1457. diff --git a/tests/scanner_integration.rs b/tests/scanner_integration.rs index 7dbc8b16d..6dcf0d102 100644 --- a/tests/scanner_integration.rs +++ b/tests/scanner_integration.rs @@ -1506,3 +1506,86 @@ fn test_scanner_preserves_license_tables_in_legal_notices() { } } } + +#[test] +fn test_scanner_rejects_unrelated_composite_licenses_in_html_headings() { + let temp_dir = tempfile::TempDir::new().expect("temporary scan root"); + let templates = [ + include_str!("../testdata/license-golden/datadriven/external/maven-bsd-heading.html") + .replace("BSD_3-Clause", "LICENSE_ANCHOR") + .replace("BSD 3-Clause", "LICENSE_NAME"), + include_str!("../testdata/license-golden/datadriven/external/maven-mpl-heading.html") + .replace("Mozilla_Public_License_2.0", "LICENSE_ANCHOR") + .replace("Mozilla Public License 2.0", "LICENSE_NAME"), + ]; + let mut expected = Vec::new(); + for (anchor, name, expression) in [ + ("BSD_3-Clause", "BSD 3-Clause", "bsd-new"), + ("BSD_2-Clause", "BSD 2-Clause", "bsd-simplified"), + ( + "Mozilla_Public_License_2.0", + "Mozilla Public License 2.0", + "mpl-2.0", + ), + ("Apache_License_2.0", "Apache License 2.0", "apache-2.0"), + ( + "Eclipse_Public_License_2.0", + "Eclipse Public License 2.0", + "epl-2.0", + ), + ] { + for (i, template) in templates.iter().enumerate() { + let filename = format!("{expression}-{i}.html"); + fs::write( + temp_dir.path().join(&filename), + template + .replace("LICENSE_ANCHOR", anchor) + .replace("LICENSE_NAME", name), + ) + .unwrap(); + expected.push((filename, expression)); + } + } + for (filename, text, expression) in [ + ( + "composite-bsd.txt", + include_str!("../testdata/license-golden/datadriven/external/maven-composite-bsd.txt"), + "bsd-new AND bsd-simplified", + ), + ( + "composite-elastic.xml", + include_str!( + "../testdata/license-golden/datadriven/external/maven-composite-elastic.xml" + ), + "elastic-license-v2 OR mongodb-sspl-1.0", + ), + ] { + fs::write(temp_dir.path().join(filename), text).unwrap(); + expected.push((filename.to_string(), expression)); + } + let result = scan( + temp_dir.path(), + 10, + &[], + create_license_detection_engine(), + false, + None, + ); + for (filename, expression) in expected { + let file = result + .files + .iter() + .find(|f| f.path.ends_with(&filename)) + .unwrap(); + assert_eq!( + file.detected_license_expression.as_deref(), + Some(expression), + "{filename}: {file:#?}" + ); + assert!( + file.license_clues.is_empty(), + "{filename}: {:?}", + file.license_clues + ); + } +}