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

  1. You build the patched artifact and upload it to a local repository.
  2. You register the artifact with POST /xray/api/v1/ztr/patches, declaring which original coordinate it replaces and which vulnerabilities it remediates.
  3. Zero-Touch Remediation validates the request and ingests the declaration into Catalog.
  4. 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.
  5. Scan results reflect Catalog data for the served patched version.

To register local patches:

  1. 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
  2. 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.

  3. Send a POST request 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
  4. 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).

    Required fields per patch item:

    FieldRequiredDescription
    local_repoYesKey of the local repository that holds the patched artifact.
    artifact_pathYesPath of the patched artifact inside local_repo. Do not start the path with /. Do not include empty, ., or .. segments, or the characters \, %, ?, or #.
    sha256YesSHA-256 of the stored artifact (64 hexadecimal characters). Must match the CycloneDX SHA-256 hash.
    declarationYesCycloneDX 1.6 BOM that identifies the original coordinate and the remediated vulnerabilities.
    item_idNoCaller-defined identifier echoed in the result so a CI job can map each outcome to a build step.

    Request-level field:

    FieldRequiredDescription
    patchesYesArray of patch items. Minimum 1, maximum 100.
    create_evidenceNoDefault false. Signed evidence is not supported in this release. When true, accepted items include an evidence_not_supported warning.
  5. Confirm the per-item status values (accepted, accepted_with_warnings, or rejected) and any messages codes. Then confirm that consuming Maven builds against the covered virtual repository receive the patched bytes, and that Remediation Requests records vendor Local.

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.

ConditionOutcomeTypical code
virtual_repo on a patch itemRejectfield_not_allowed
Local repository is not indexed in XrayRejectrepo_not_indexed
Named repository is not a local repositoryRejectrepo_not_local
Named repository does not existRejectrepo_not_found
Artifact is missing at artifact_pathRejectartifact_not_found
SHA-256 does not match the stored artifact or the CycloneDX subjectRejectsubject_mismatch
CycloneDX is malformed, specVersion is not 1.6, or the patched version is not {original}-local.<digits> / {original}.local.<digits>Rejectmalformed_declaration
A required original-coordinate or OneModel property is missingRejectmissing_property
Caller lacks Manage Data permission on the artifactRejectpermission_denied
Catalog ingest fails after validationRejectcatalog_ingest_failed
create_evidence is trueAccept with warningevidence_not_supported
📘

Request-level limits

More than 100 patches or a body larger than 1 MiB returns HTTP 413. Invalid JSON, an empty patches array, or a request-level virtual_repo field 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


Did this page help you?