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
34 changes: 34 additions & 0 deletions antora/docs/modules/ROOT/pages/packages/release_maven_repos.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
= All maven artifacts have known repository URLs Package

Each Maven package listed in an SBOM must specify the repository URL that it comes from, and that URL must be present in the list of known and permitted Maven repositories. If no URL is specified, the package is assumed to come from Maven Central.

== Package Name

* `maven_repos`

== Rules Included

[#maven_repos__deny_unpermitted_urls]
=== link:#maven_repos__deny_unpermitted_urls[Known Repository URLs]

Each Maven package listed in an SBOM must specify the repository URL that it comes from, and that URL must be present in the list of known and permitted Maven repositories. If no URL is specified, the package is assumed to come from Maven Central.

*Solution*: The Maven artifact originates from an untrusted or unpermitted repository. To resolve this, ensure the dependency is sourced from a repository defined in the 'allowed_maven_repositories' list in your policy configuration. If the repository is internal, add its URL to the allowed list in rule_data.

* Rule type: [rule-type-indicator failure]#FAILURE#
* FAILURE message: `%s`
* Code: `maven_repos.deny_unpermitted_urls`
* Effective from: `2026-05-10T00:00:00Z`
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/maven_repos/maven_repos.rego#L35[Source, window="_blank"]

[#maven_repos__policy_data_missing]
=== link:#maven_repos__policy_data_missing[Policy data validation]

Ensures the required allowed_maven_repositories list is provided.

*Solution*: Ensure that 'allowed_maven_repositories' is defined in the rule_data provided to the policy, and that it contains a list of authorized repository URLs.

* Rule type: [rule-type-indicator failure]#FAILURE#
* FAILURE message: `Policy data is missing the required "%s" list`
* Code: `maven_repos.policy_data_missing`
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/maven_repos/maven_repos.rego#L17[Source, window="_blank"]
12 changes: 12 additions & 0 deletions antora/docs/modules/ROOT/pages/release_policy.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ a| Include policy rules responsible for validating rule data.

Rules included:

* xref:packages/release_maven_repos.adoc#maven_repos__policy_data_missing[All maven artifacts have known repository URLs: Policy data validation]
* xref:packages/release_attestation_type.adoc#attestation_type__known_attestation_types_provided[Attestation type: Known attestation types provided]
* xref:packages/release_base_image_registries.adoc#base_image_registries__allowed_registries_provided[Base image checks: Allowed base image registry prefixes list was provided]
* xref:packages/release_buildah_build_task.adoc#buildah_build_task__disallowed_platform_patterns_pattern[Buildah build task: disallowed_platform_patterns format]
Expand Down Expand Up @@ -236,6 +237,14 @@ Rules included:
* xref:packages/release_rpm_ostree_task.adoc#rpm_ostree_task__builder_image_param[rpm-ostree Task: Builder image parameter]
* xref:packages/release_rpm_ostree_task.adoc#rpm_ostree_task__rule_data[rpm-ostree Task: Rule data]

| [#redhat_maven]`redhat_maven`
a| Ruleset for validating artifacts built via Red Hat Maven repositories.

Rules included:

* xref:packages/release_maven_repos.adoc#maven_repos__deny_unpermitted_urls[All maven artifacts have known repository URLs: Known Repository URLs]
* xref:packages/release_maven_repos.adoc#maven_repos__policy_data_missing[All maven artifacts have known repository URLs: Policy data validation]

| [#redhat_rpms]`redhat_rpms`
a| Include the set of policy rules required for building Red Hat RPMs.

Expand Down Expand Up @@ -339,6 +348,9 @@ Rules included:
|*Package Name*
|*Description*

| xref:packages/release_maven_repos.adoc[maven_repos]
a| Each Maven package listed in an SBOM must specify the repository URL that it comes from, and that URL must be present in the list of known and permitted Maven repositories. If no URL is specified, the package is assumed to come from Maven Central.

| xref:packages/release_attestation_type.adoc[attestation_type]
a| Sanity checks related to the format of the image build's attestation.

Expand Down
4 changes: 4 additions & 0 deletions antora/docs/modules/ROOT/partials/release_policy_nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@
*** xref:release_policy.adoc#minimal[minimal]
*** xref:release_policy.adoc#policy_data[policy_data]
*** xref:release_policy.adoc#redhat[redhat]
*** xref:release_policy.adoc#redhat_maven[redhat_maven]
*** xref:release_policy.adoc#redhat_rpms[redhat_rpms]
*** xref:release_policy.adoc#rhtap-multi-ci[rhtap-multi-ci]
*** xref:release_policy.adoc#slsa3[slsa3]
** Release Rules
*** xref:packages/release_maven_repos.adoc[All maven artifacts have known repository URLs]
**** xref:packages/release_maven_repos.adoc#maven_repos__deny_unpermitted_urls[Known Repository URLs]
**** xref:packages/release_maven_repos.adoc#maven_repos__policy_data_missing[Policy data validation]
*** xref:packages/release_attestation_type.adoc[Attestation type]
**** xref:packages/release_attestation_type.adoc#attestation_type__deprecated_policy_attestation_format[Deprecated policy attestation format]
**** xref:packages/release_attestation_type.adoc#attestation_type__known_attestation_type[Known attestation type found]
Expand Down
66 changes: 66 additions & 0 deletions policy/lib/sbom/maven.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# METADATA
# title: Maven Package Extraction
# description: >-
# Extracts Maven packages and their repository URLs from both CycloneDX
# and SPDX SBOM formats.
package lib.sbom

import rego.v1

maven_packages contains pkg if {
some pkg in _cyclonedx_maven_packages
}

maven_packages contains pkg if {
some pkg in _spdx_maven_packages
}

_cyclonedx_maven_packages contains pkg if {
some s in cyclonedx_sboms
some component in s.components

startswith(component.purl, "pkg:maven/")

repos := {ref.url |
some ref in component.externalRefs
ref.type in {"distribution", "artifact-repository"}
}

final_repos := _empty_to_default(repos)

some repo_url in final_repos
pkg := {
"purl": component.purl,
"name": component.name,
"repository_url": repo_url,
}
}

_spdx_maven_packages contains pkg if {
some s in spdx_sboms
some item in s.packages

startswith(item.purl, "pkg:maven/")

repos := {ref.referenceLocator |
some ref in item.externalRefs
ref.referenceType in {"distribution", "repository"}
}

final_repos := _empty_to_default(repos)

some repo_url in final_repos
pkg := {
"purl": item.purl,
"name": item.name,
"repository_url": repo_url,
}
}

# _empty_to_default ensures that packages without explicit repository URLs
# are still processed. If the input repo_set is empty, it returns {""}.
# In the context of this policy, a blank repository URL is considered
# to be Maven Central (https://repo.maven.apache.org/maven2/).
_empty_to_default(repo_set) := repo_set if {
Comment thread
csasalu marked this conversation as resolved.
count(repo_set) > 0
} else := {""}
134 changes: 134 additions & 0 deletions policy/lib/sbom/maven_test.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package lib.sbom_test

import data.lib.assertions
import data.lib.sbom

test_cyclonedx_maven_extraction if {
mock_components := [{
"name": "auth-lib",
"purl": "pkg:maven/org.example/auth@1.0",
"externalRefs": [{"type": "distribution", "url": "https://repo.maven.apache.org/maven2/"}],
}]

expected := {{
"name": "auth-lib",
"purl": "pkg:maven/org.example/auth@1.0",
"repository_url": "https://repo.maven.apache.org/maven2/",
}}
Comment thread
csasalu marked this conversation as resolved.

result := sbom.maven_packages with sbom.cyclonedx_sboms as [_cyclonedx_sbom(mock_components)]

assertions.assert_equal(expected, result)
}

test_cyclonedx_ignores_non_maven if {
mock_components := [{"name": "react", "purl": "pkg:npm/react@18.2.0"}]

assertions.assert_empty(sbom.maven_packages) with sbom.cyclonedx_sboms as [_cyclonedx_sbom(mock_components)]
}

test_cyclonedx_empty_repo_url if {
mock_components := [{
"name": "no-repo",
"purl": "pkg:maven/org.example/no-repo@1.0",
"externalRefs": [],
}]

expected := {{
"name": "no-repo",
"purl": "pkg:maven/org.example/no-repo@1.0",
"repository_url": "",
}}

result := sbom.maven_packages with sbom.cyclonedx_sboms as [_cyclonedx_sbom(mock_components)]

assertions.assert_equal(expected, result)
}

test_spdx_maven_extraction if {
mock_packages := [{
"name": "data-service",
"purl": "pkg:maven/org.example/data@2.5",
"externalRefs": [{
"referenceType": "repository",
"referenceLocator": "https://internal.jfrog.io/artifactory",
}],
}]

expected := {{
"name": "data-service",
"purl": "pkg:maven/org.example/data@2.5",
"repository_url": "https://internal.jfrog.io/artifactory",
}}
Comment thread
csasalu marked this conversation as resolved.

result := sbom.maven_packages with sbom.spdx_sboms as [_spdx_sbom(mock_packages)]

assertions.assert_equal(expected, result)
}

test_combined_sources if {
mock_cdx := [{
"name": "cdx-pkg",
"purl": "pkg:maven/cdx/pkg@1",
"externalRefs": [{"type": "distribution", "url": "url1"}],
}]

mock_spdx := [{
"name": "spdx-pkg",
"purl": "pkg:maven/spdx/pkg@1",
"externalRefs": [{
"referenceType": "repository",
"referenceLocator": "url2",
}],
}]

expected := {
{
"name": "cdx-pkg",
"purl": "pkg:maven/cdx/pkg@1",
"repository_url": "url1",
},
{
"name": "spdx-pkg",
"purl": "pkg:maven/spdx/pkg@1",
"repository_url": "url2",
},
}

result := sbom.maven_packages with sbom.cyclonedx_sboms as [_cyclonedx_sbom(mock_cdx)]
with sbom.spdx_sboms as [_spdx_sbom(mock_spdx)]

assertions.assert_equal(expected, result)
}

test_cyclonedx_multiple_repo_capture if {
mock_components := [{
"name": "multi-repo-lib",
"purl": "pkg:maven/org.example/multi@1.0",
"externalRefs": [
{"type": "distribution", "url": "https://repo-a.com"},
{"type": "artifact-repository", "url": "https://repo-b.com"},
],
}]

expected := {
{
"name": "multi-repo-lib",
"purl": "pkg:maven/org.example/multi@1.0",
"repository_url": "https://repo-a.com",
},
{
"name": "multi-repo-lib",
"purl": "pkg:maven/org.example/multi@1.0",
"repository_url": "https://repo-b.com",
},
}

result := sbom.maven_packages with sbom.cyclonedx_sboms as [_cyclonedx_sbom(mock_components)]

assertions.assert_equal(expected, result)
}

_cyclonedx_sbom(components) := {"components": components}

_spdx_sbom(packages) := {"packages": packages}
8 changes: 8 additions & 0 deletions policy/release/collection/redhat_maven/redhat_maven.rego
Comment thread
st3penta marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#
# METADATA
# title: redhat_maven
Comment thread
st3penta marked this conversation as resolved.
# description: >-
# Ruleset for validating artifacts built via Red Hat Maven repositories.
package collection.redhat_maven

import rego.v1
Comment thread
csasalu marked this conversation as resolved.
88 changes: 88 additions & 0 deletions policy/release/maven_repos/maven_repos.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# METADATA
# title: All maven artifacts have known repository URLs
# description: >-
# Each Maven package listed in an SBOM must specify the repository URL that it
# comes from, and that URL must be present in the list of known and permitted
# Maven repositories. If no URL is specified, the package is assumed to come
# from Maven Central.
package release.maven_repos

import rego.v1

import data.lib
import data.lib.metadata
import data.lib.rule_data
import data.lib.sbom

# METADATA
# title: Policy data validation
# description: Ensures the required allowed_maven_repositories list is provided.
# custom:
# short_name: policy_data_missing
# failure_msg: Policy data is missing the required "%s" list
# solution: >-
# Ensure that 'allowed_maven_repositories' is defined in the rule_data
# provided to the policy, and that it contains a list of authorized
# repository URLs.
# collections:
# - redhat_maven
# - policy_data
Comment thread
st3penta marked this conversation as resolved.
deny contains result if {
some key in _rule_data_errors
result := lib.result_helper(rego.metadata.chain(), [key])
}

# METADATA
# title: Known Repository URLs
# description: >-
# Each Maven package listed in an SBOM must specify the repository URL that it
# comes from, and that URL must be present in the list of known and permitted
# Maven repositories. If no URL is specified, the package is assumed to come
# from Maven Central.
# custom:
Comment thread
csasalu marked this conversation as resolved.
# short_name: deny_unpermitted_urls
# failure_msg: '%s'
# solution: >-
# The Maven artifact originates from an untrusted or unpermitted repository.
# To resolve this, ensure the dependency is sourced from a repository defined
# in the 'allowed_maven_repositories' list in your policy configuration.
# If the repository is internal, add its URL to the allowed list in rule_data.
# effective_on: 2026-05-10T00:00:00Z
Comment thread
simonbaird marked this conversation as resolved.
# collections:
# - redhat_maven
deny contains result if {
some err in _repo_url_errors
result := metadata.result_helper_with_term(rego.metadata.chain(), [err.msg], err.purl)
}

_repo_url_errors contains err if {
some pkg in sbom.maven_packages
source := _get_effective_url(pkg.repository_url)
not _url_is_permitted(source)
err := {
"purl": pkg.purl,
"msg": sprintf("Package %q (source: %q) is not in the permitted list", [pkg.purl, source]),
}
}
Comment thread
csasalu marked this conversation as resolved.

_get_effective_url(url) := url if {
url != ""
} else := "https://repo.maven.apache.org/maven2/"

_url_is_permitted(url) if {
permitted := rule_data.get("allowed_maven_repositories")
url in permitted
}

_rule_data_errors contains key if {
key := "allowed_maven_repositories"
data_list := rule_data.get(key)
_is_invalid_data(data_list)
}

_is_invalid_data(val) if not is_array(val)

_is_invalid_data(val) if {
is_array(val)
count(val) == 0
}
Loading
Loading