XRAY: How "jf docker scan" for Local Images Works

Products
JFrog_Xray
Content Type
Use_Case
AuthorFullName__c
Kanishk Sharma
articleNumber
000006812
FirstPublishedDate
2025-12-31T09:53:26Z
lastModifiedDate
2025-12-31

XRAY: How "jf docker scan" for Local Images Works

Introduction

In this article, we will explore when scanning a local Docker image (one that is on the local machine and not in Artifactory) using the jf docker scan <image:tag> command, what exactly is sent to the JFrog Platform? Is the entire, potentially multi-gigabyte, Docker image uploaded?


Working:


The jf docker scan command does not upload the full Docker image to the server. It performs a hybrid scan process:
  1. Local Archiving & Indexing: The CLI creates a local archive of the image and uses a local indexing engine (jfxia) to build a complete component graph (a Bill of Materials).
  2. Server-Side Vulnerability Matching: The CLI uploads only this component graph (metadata) to the JFrog Platform, which matches it against the Xray vulnerability database.
  3. Local Advanced Security Scan: The server sends the list of found vulnerabilities back to the CLI. The CLI then runs a local analyzer (analyzerManager) to perform JFrog Advanced Security (JAS) scans, such as Contextual Analysis (Applicability) and Secrets Detection, on the local archive.
This process is fast, preserves network bandwidth, and keeps your source code and secrets on the local machine for analysis.


Step-by-Step Process


Here is the exact sequence of events when you run  jf docker scan:
  1. Connection & Entitlement Check: The CLI first contacts the configured JFrog Platform URL (e.g., https://<ARTIFACTORY_URL>/). It makes several API calls to check versions (/artifactory/api/system/version, /xray/api/v1/system/version) and, crucially, to verify entitlements and fetch configurations for JFrog Advanced Security (/xray/api/v1/entitlements/feature/contextual_analysis, /xray/api/v1/configuration/jas).
  2. Verify/Download Local Analyzer: The CLI checks for a local copy of the analyzerManager. If it's missing or outdated, it downloads it from JFrog (e.g., releases.jfrog.io). This analyzer is responsible for the local indexing and analysis.
  3. Local Image Archiving: The CLI displays "Creating image archive". It reads the layers of the specified local Docker image (docker/desktop-kubernetes in this case) and creates a temporary image.tar file in a local temp directory (e.g., /var/folders/bx/.../image.tar).
  4. Local Indexing (jfxia): The CLI displays "Indexing file...". It now invokes the local analyzerManager (jfxia), which scans the image.tar file. It inspects each layer, identifies binaries (like Go), and builds a complete graph of all components and their dependencies.
  5. Metadata Upload & Server-Side Scan: The CLI uploads the component graph (metadata) only via a POST request to the /xray/api/v1/xsc/sca/scan/graph endpoint. The Xray backend receives this graph and matches it against its vulnerability database.
  6. Polling for Vulnerability List: The CLI displays "Scanning..." (or "Waiting for scan to complete...") while it polls the /xray/api/v1/xsc/sca/scan/graph/{scan_id} endpoint, waiting for the server to finish its analysis and return the list of vulnerabilities.
  7. Local Analysis (Contextual & Secrets): Once the CLI receives the vulnerability list, it again invokes the local analyzerManager.
    • Contextual Analysis: It passes the list of found CVEs to the analyzer (e.g., cve-whitelist: [CVE-2025-61723, ...]) and runs an analyze-applicability-docker-scan to determine which CVEs are actually "Applicable".
    • Secrets Detection: It runs a secrets-docker-scan to find any exposed secrets in the image layers.
  8. Displaying Results & Telemetry: Finally, the CLI combines the vulnerability list from the server with the local Contextual Analysis and Secrets results, formatting them into the tables you see in your console. It then sends a final PUT request to /xray/api/v1/xsc/event to log the scan completion.

Example: Debug Log Analysis


This entire process can be observed by running the scan with the --log-level=DEBUG flag. The following output from the full log file (jfrog-cli.2025-11-18...log) illustrates this hybrid process.
$ jf docker scan docker/desktop-kubernetes --log-level=DEBUG
02:49:10 [Debug] JFrog CLI version: 2.82.0
02:49:10 [Debug] Using <test> server-id configuration

Step 1: Connection & Entitlement Check
These logs show the initial API calls to Artifactory and Xray to verify versions and check for JAS entitlements.
[Debug] Sending HTTP GET request to: [https://<ARTIFACTORY_URL>/artifactory/api/system/version](https://<ARTIFACTORY_URL>/artifactory/api/system/version)
[Debug] Sending HTTP GET request to: [https://<ARTIFACTORY_URL>/xray/api/v1/system/version](https://<ARTIFACTORY_URL>/xray/api/v1/system/version)
[Debug] Sending HTTP GET request to: [https://<ARTIFACTORY_URL>/xray/api/v1/entitlements/feature/contextual_analysis](https://<ARTIFACTORY_URL>/xray/api/v1/entitlements/feature/contextual_analysis)
[Debug] Sending HTTP GET request to: [https://<ARTIFACTORY_URL>/xray/api/v1/configuration/jas](https://<ARTIFACTORY_URL>/xray/api/v1/configuration/jas)

Step 2: Verify/Download Local Analyzer
The CLI checks for the local analyzerManager and confirms where to download it from if it's missing.
[Debug] 'JFROG_CLI_RELEASES_REPO' environment variable is not configured. The Analyzer Manager will be downloaded directly from releases.jfrog.io if needed.
[Debug] Sending HTTP HEAD request to: [https://releases.jfrog.io/artifactory/xsc-gen-exe-analyzer-manager-local/v1/1.23.9/mac-arm64/analyzerManager.zip](https://releases.jfrog.io/artifactory/xsc-gen-exe-analyzer-manager-local/v1/1.23.9/mac-arm64/analyzerManager.zip)

Step 3: Local Image Archiving
The CLI begins the process of creating a local image.tar file from the Docker image.
[Info] Creating image archive...

Step 4: Local Indexing (jfxia) The local analyzerManager (jfxia) is invoked to index the temporary image.tar file, scan each layer, and identify components.
[Info] [Thread 0] Indexing file: /var/folders/bx/3h3hsb_x2_16qgjk3q1svd2c0000gp/T/jfrog.cli.temp.-1763414350-1735073609/image.tar
2025-11-17T21:19:15.565Z [jfxia] [DEBUG] [] [archive_mgr:124...] Indexing standalone file /var/folders/.../image.tar
2025-11-17T21:19:16.191Z [jfxia] [DEBUG] [] [docker:170...] Scanning docker layer 'sha256__e1a06c80c...tar'
2025-11-17T21:19:17.159Z [jfxia] [INFO ] [] [golang:53...] couldn't extract golang version. ldflags are empty
2025-11-17T21:19:22.335Z [jfxia] [INFO ] [] [docker:108...] Finished indexing layers of docker /var/folders/.../

Step 5: Metadata Upload & Server-Side Scan The component graph (metadata) is POSTed to the Xray/XSC endpoint to be matched against the vulnerability database.
[Debug] Sending HTTP POST request to: [https://<ARTIFACTORY_URL>/xray/api/v1/xsc/sca/scan/graph?multi_scan_id=76e274ce...&scan_type=binary](https://<ARTIFACTORY_URL>/xray/api/v1/xsc/sca/scan/graph?multi_scan_id=76e274ce...&scan_type=binary)
[Info] Waiting for scan to complete on JFrog Xray...

Step 6: Polling for Vulnerability List The CLI repeatedly polls the scan graph endpoint until the server-side analysis is complete and the vulnerability list is ready.
[Debug] Sending HTTP GET request to: [https://<ARTIFACTORY_URL>/xray/api/v1/xsc/sca/scan/graph/ad1f7a28-1c67-4a4a-9184-d1b9dfd439e9?include_vulnerabilities=true](https://<ARTIFACTORY_URL>/xray/api/v1/xsc/sca/scan/graph/ad1f7a28-1c67-4a4a-9184-d1b9dfd439e9?include_vulnerabilities=true)
[Debug] Get Dependencies Scan results... (Attempt 1)
[Debug] Sending HTTP GET request to: [https://<ARTIFACTORY_URL>/xray/api/v1/xsc/sca/scan/graph/ad1f7a28-1c67-4a4a-9184-d1b9dfd439e9?include_vulnerabilities=true](https://<ARTIFACTORY_URL>/xray/api/v1/xsc/sca/scan/graph/ad1f7a28-1c67-4a4a-9184-d1b9dfd439e9?include_vulnerabilities=true)
[Debug] Get Dependencies Scan results... (Attempt 2)
...

Step 7: Local Analysis (Contextual & Secrets) Using the vulnerability list from the server, the local analyzerManager is invoked again to perform Contextual Analysis (Applicability) and Secrets Detection.
[Debug] Using analyzer manager executable at: /Users/kanishks/.jfrog/dependencies/analyzerManager/analyzerManager
[Info] [Thread 0] Running Contextual Analysis scan on target '/var/folders/.../image.tar'...
[Debug] Applicability scanner input YAML:
scans:
    - roots:
        - /var/folders/.../image.tar
      type: analyze-applicability-docker-scan
      cve-whitelist:
        - CVE-2025-61723
        - CVE-2025-58186
        ...
[Info] [Thread 0] Found 6 applicable cves
[Info] [Thread 0] Running Secrets scan on target '/var/folders/.../image.tar'...
[Debug] Secrets scanner input YAML:
scans:
    - roots:
        - /var/folders/.../image.tar
      type: secrets-docker-scan
[Info] [Thread 0] Found 1 Secrets exposures

Step 8: Displaying Results & Telemetry The CLI aggregates all results, displays them to the user, and sends a final event to log the scan's completion.
Vulnerable Components
┌────────────────┬────────────┬────────────────┬───────────────────...
...
(Vulnerability table)
...
Secrets Detection
┌──────────┬──────────────────────────┬─────────────┬────────┬───...
...
(Secrets table)
...
[Debug] Sending HTTP PUT request to: [https://<ARTIFACTORY_URL>/xray/api/v1/xsc/event](https://<ARTIFACTORY_URL>/xray/api/v1/xsc/event)
[Debug] Command event:
{{0 completed    35 0 false      1m8.995504125s  } 76e274ce-2295-4d6a-bcd7-f3226a54d317 }
[🔵Info] Scan completed successfully.

Conclusion

The jf docker scan command for local images uses an efficient hybrid model. It intelligently combines local indexing and advanced analysis with server-side vulnerability database matching. This approach ensures that your full Docker image and any sensitive content remain on your local machine, while still leveraging the power and comprehensive data of the JFrog Platform.