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
+ );
+ }
+}