Register Local Patches
Register internally rebuilt Maven artifacts so Zero-Touch Remediation can serve them at the original coordinate—without changing consuming builds.
Some organizations backport security fixes themselves instead of consuming a clearing-house rebuild. When those patched Maven artifacts already sit in an Artifactory local repository, Zero-Touch Remediation can serve them at the original coordinate: no pom.xml change, Catalog-backed candidate selection, scan reconciliation, and an audit trail.
Registration is a REST API request. After a patch is accepted, Catalog can return it as a candidate. Artifactory serves it only when a covered Maven virtual repository includes that local repository as a member.
Local patches are Maven only. npm lockfile healing does not consume locally registered patches.
Prerequisites
- Platform access with Manage Data permission on each artifact you register.
- A patched Maven artifact uploaded to a local Artifactory repository.
- That local repository is indexed in Xray.
- That local repository is a member of at least one Maven virtual repository covered by an active Zero-Touch Remediation automation. Membership is what allows Artifactory to serve the patched bytes at the original coordinate; the registration request does not name the virtual repository.
- JFrog Catalog access. Zero-Touch Remediation translates your CycloneDX declaration into Catalog ingest so the patch becomes a selectable candidate.
How Registration Fits the Remediation Flow
- You build the patched artifact and upload it to a local repository.
- You register the artifact with
POST /xray/api/v1/ztr/patches, declaring which original coordinate it replaces and which vulnerabilities it remediates. - Zero-Touch Remediation validates the request and ingests the declaration into Catalog.
- On a later download of the original coordinate from a covered virtual repository that includes the local repository as a member, Xray can select the local patch using Least Vulnerable. When scores are equal, a local patch ranks above a vendor rebuild.
- Scan results reflect Catalog data for the served patched version.
To register local patches:
-
Upload the patched Maven artifact to an Xray-indexed local repository that is a member of a covered Maven virtual repository. For example:
jf rt upload spring-core-5.3.39-local.1.jar \ patches-local/org/springframework/spring-core/5.3.39-local.1/spring-core-5.3.39-local.1.jar -
Record the artifact path relative to the local repository and the artifact SHA-256. The SHA-256 must match both the stored file and the CycloneDX component hash.
-
Send a
POSTrequest to/xray/api/v1/ztr/patches. One request can register up to 100 patches. The serialized body must not exceed 1 MiB.Authentication uses a bearer token or HTTP basic authentication. Inspect every item in
results. HTTP 200 means the batch was processed, not that every patch was stored.curl -X POST \ -H "Authorization: Bearer <access-token>" \ -H "Content-Type: application/json" \ "https://<jpd>/xray/api/v1/ztr/patches" \ -d @local-patches.json -
Use a CycloneDX 1.6 declaration for each patch item. Exactly one component is the patched component. Its version must extend the original version with
-local.<digits>or.local.<digits>(for example5.3.39-local.1).Required fields per patch item:
Field Required Description local_repoYes Key of the local repository that holds the patched artifact. artifact_pathYes Path of the patched artifact inside local_repo. Do not start the path with/. Do not include empty,., or..segments, or the characters\,%,?, or#.sha256Yes SHA-256 of the stored artifact (64 hexadecimal characters). Must match the CycloneDX SHA-256 hash. declarationYes CycloneDX 1.6 BOM that identifies the original coordinate and the remediated vulnerabilities. item_idNo Caller-defined identifier echoed in the result so a CI job can map each outcome to a build step. Request-level field:
Field Required Description patchesYes Array of patch items. Minimum 1, maximum 100. create_evidenceNo Default false. Signed evidence is not supported in this release. Whentrue, accepted items include anevidence_not_supportedwarning. -
Confirm the per-item
statusvalues (accepted,accepted_with_warnings, orrejected) and anymessagescodes. Then confirm that consuming Maven builds against the covered virtual repository receive the patched bytes, and that Remediation Requests records vendorLocal.
Example request body
{
"create_evidence": false,
"patches": [
{
"item_id": "item-1",
"local_repo": "patches-local",
"artifact_path": "org/springframework/spring-core/5.3.39-local.1/spring-core-5.3.39-local.1.jar",
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"declaration": {
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"metadata": {
"component": {
"bom-ref": "pkg:maven/org.springframework/[email protected]",
"type": "library",
"name": "org.springframework:spring-core",
"version": "5.3.39-local.1",
"purl": "pkg:maven/org.springframework/[email protected]"
}
},
"components": [
{
"bom-ref": "pkg:maven/org.springframework/[email protected]",
"type": "library",
"name": "org.springframework:spring-core",
"version": "5.3.39-local.1",
"purl": "pkg:maven/org.springframework/[email protected]",
"hashes": [
{
"alg": "SHA-256",
"content": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
],
"properties": [
{ "name": "jfrog:component:original_name", "value": "org.springframework:spring-core" },
{ "name": "jfrog:component:original_version", "value": "5.3.39" },
{ "name": "jfrog:component:original_purl", "value": "pkg:maven/org.springframework/[email protected]" },
{ "name": "jfrog:component:onemodel_name", "value": "spring-core" },
{ "name": "jfrog:component:onemodel_type", "value": "maven" },
{ "name": "jfrog:component:onemodel_ecosystem", "value": "generic" },
{ "name": "jfrog:component:onemodel_namespace", "value": "org.springframework" },
{ "name": "jfrog:component:onemodel_version", "value": "5.3.39-local.1" }
]
}
],
"vulnerabilities": [
{
"id": "CVE-2024-22259",
"analysis": { "state": "resolved" },
"affects": [
{ "ref": "pkg:maven/org.springframework/[email protected]" }
]
}
]
}
}
]
}The patched component must include the original-coordinate properties (jfrog:component:original_name, original_version, original_purl) and the OneModel properties (onemodel_name, onemodel_type, onemodel_ecosystem, onemodel_namespace, onemodel_version). onemodel_type is maven and onemodel_ecosystem is generic. metadata.component must match the patched component identity. Each vulnerability affects.ref must match a declared bom-ref, and at least one must identify the patched component.
Example response
A mixed batch still returns HTTP 200. Read results rather than inferring success from the status code.
{
"accepted": 1,
"rejected": 1,
"results": [
{
"item_id": "item-1",
"local_repo": "patches-local",
"artifact_path": "org/springframework/spring-core/5.3.39-local.1/spring-core-5.3.39-local.1.jar",
"status": "accepted",
"messages": []
},
{
"item_id": "item-2",
"local_repo": "patches-local",
"artifact_path": "org/example/example/1.0.0-local.1/example-1.0.0-local.1.jar",
"status": "rejected",
"messages": [
{
"code": "subject_mismatch",
"severity": "error",
"message": "artifact sha256 does not match request sha256",
"field": "/patches/1/sha256"
}
]
}
]
}accepted counts both accepted and accepted_with_warnings items. An item-level failure does not roll back items that succeeded. A request-level failure (invalid JSON, empty patches array, a virtual_repo field, authentication failure, payload over 1 MiB or 100 items) rejects the entire request and stores nothing.
Do not send virtual_repo. The field is not accepted: at request level it returns HTTP 400; on an individual patch item it rejects that item with field_not_allowed.
Validation Outcomes
Zero-Touch Remediation rejects a patch when the registration cannot work. The only warning this release returns on an otherwise accepted item is evidence_not_supported when create_evidence is true.
| Condition | Outcome | Typical code |
|---|---|---|
virtual_repo on a patch item | Reject | field_not_allowed |
| Local repository is not indexed in Xray | Reject | repo_not_indexed |
| Named repository is not a local repository | Reject | repo_not_local |
| Named repository does not exist | Reject | repo_not_found |
Artifact is missing at artifact_path | Reject | artifact_not_found |
| SHA-256 does not match the stored artifact or the CycloneDX subject | Reject | subject_mismatch |
CycloneDX is malformed, specVersion is not 1.6, or the patched version is not {original}-local.<digits> / {original}.local.<digits> | Reject | malformed_declaration |
| A required original-coordinate or OneModel property is missing | Reject | missing_property |
| Caller lacks Manage Data permission on the artifact | Reject | permission_denied |
| Catalog ingest fails after validation | Reject | catalog_ingest_failed |
create_evidence is true | Accept with warning | evidence_not_supported |
Request-level limitsMore than 100 patches or a body larger than 1 MiB returns HTTP 413. Invalid JSON, an empty
patchesarray, or a request-levelvirtual_repofield returns HTTP 400. If Zero-Touch Remediation is disabled, the API returns HTTP 404.
After a patch is accepted, Catalog can return it when the covered Maven virtual repository lists that local repository as a member (Artifactory sends a blank URL for local members). Removing the local repository from the virtual repository stops serving those patches. There is no separate local-patch control on the automation Scope step.
Related Topics
Updated about 5 hours ago
