Register Local Patches
Register rebuilt Maven artifacts so Zero-Touch Remediation can serve local patches at the original coordinate without changing 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, JFrog 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.
How 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:
jf rt upload <PATCHED_FILE> \ <LOCAL_REPO>/<ARTIFACT_PATH>Where:
<PATCHED_FILE>: The patched Maven artifact file<LOCAL_REPO>: The key of the Xray-indexed local repository<ARTIFACT_PATH>: The path of the artifact inside the local 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.jarThe artifact is stored in the local repository.
-
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. You have the path and SHA-256 required for the registration request.
-
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 <TOKEN>" \ -H "Content-Type: application/json" \ "https://<JFROG_PLATFORM_URL>/xray/api/v1/ztr/patches" \ -d @local-patches.jsonWhere:
<TOKEN>: Your JFrog access token<JFROG_PLATFORM_URL>: The URL of your JFrog Platform
For example:
curl -X POST \ -H "Authorization: Bearer <TOKEN>" \ -H "Content-Type: application/json" \ "https://company.jfrog.io/xray/api/v1/ztr/patches" \ -d @local-patches.jsonThe API returns a JSON body with per-item
results. -
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 example,5.3.39-local.1.The following fields are required 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. The following field applies at request level.
Field Required Description patchesYes Array of patch items. Minimum one, maximum 100. create_evidenceNo Default false. Set totrueto create signed evidence for each accepted patch. Evidence creation doesn't reject the patch. -
Confirm the per-item
statusvalues ofaccepted,accepted_with_warnings, orrejected, and anymessagescodes. Accepted items are stored as Catalog candidates.Each accepted item includes an
evidenceobject. Rejected items omit it. Whencreate_evidenceisfalse,evidence.statusisskippedandevidence.reasonisnot_requested. The item status staysaccepted.When
create_evidenceistrueand evidence is created,evidence.statusiscreatedand the object includesidanduri. When evidence is skipped or fails, the item status isaccepted_with_warnings. The warningcodeisevidence_skippedorevidence_failed, andfieldis/create_evidence.evidence.reasonexplains the outcome.Note
Signed evidence requires an Enterprise Plus or Enterprise Plus Trial subscription, JFrog Artifactory 7.104.2 or later, and JFrog Evidence 7.232.0 or later. Your JFrog Platform also needs Zero-Touch Remediation and the Unified Security Bundle or the Ultimate Security Bundle.
You can confirm that consuming Maven builds against the covered virtual repository receive the patched bytes. For more information, see View Remediation Requests.
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 rejects the entire request and stores nothing. Request-level failures include invalid JSON, an empty patches array, a virtual_repo field, authentication failure, and a payload over 1 MiB or 100 items. If Zero-Touch Remediation is disabled, the API returns HTTP 404.
Don't 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. A skipped or failed evidence request is a warning on an accepted item. The patch is still stored. The following conditions describe typical validation outcomes.
| 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 and evidence is skipped or fails | Accept with warning | evidence_skipped or evidence_failed |
Note
More 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.
Frequently Asked Questions
This section provides answers to frequently asked questions.
FAQs
Q: How do I register a local Maven patch?
A: Upload the patched artifact to an Xray-indexed local repository, then send POST /xray/api/v1/ztr/patches with a CycloneDX 1.6 declaration. For more information, see How Registration Fits the Remediation Flow.
Q: What happens if the SHA-256 in the request does not match the stored file?
A: Zero-Touch Remediation rejects that item with subject_mismatch. HTTP 200 on the batch does not mean every item was stored. For more information, see Validation Outcomes.
Q: What is the difference between HTTP 200 and an accepted patch?
A: HTTP 200 means the batch was processed. Inspect every results item for accepted, accepted_with_warnings, or rejected. For more information, see Example Response.
Q: What permissions do I need to register local patches?
A: You need Manage Data permission on each artifact you register, plus an Xray-indexed Maven local repository that is a member of a covered virtual repository. For more information, see Prerequisites.
Q: How do I create signed evidence for a local patch?
A: Set create_evidence to true on POST /xray/api/v1/ztr/patches. An accepted item returns evidence.status of created, with id and uri. If evidence is skipped or fails, the patch is still accepted and the item status is accepted_with_warnings. For more information, see How to register local patches.
Q: Can npm lockfile healing use local patches?
A: No. Local patches are Maven only. npm lockfile healing does not consume locally registered patches. For more information, see Zero-Touch Remediation Overview.
Related Topics
Updated 15 days ago
