Agent Skill · JFrog

jfrog-package-safety-and-download

Check JFrog Public Catalog and stored packages for a version, interpret catalog security signals, and download through Artifactory (JFrog Platform locations, remote cache, curation-aware package managers, or repo proxy). Use when the user asks whether a package is safe, allowed, curated, or wants to download npm, Maven, PyPI, Go, or similar packages via JFrog. Do NOT use for pure CVE or vulnerability lookups (e.g. "details on CVE-2021-23337") — those are handled by the jfrog skill's Public security domain queries without this workflow.

Provider: JFrog Path in repo: skills/jfrog-package-safety-and-download/SKILL.md

Skill body

JFrog Package Safety and Download

Prerequisites

Workflow

Package safety check and download workflow

When to read this file:

Workflow overview

flowchart TD
    A[User requests package check / download] --> B{Package in Public Catalog?}
    B -->|Yes| C[Get latest version from Catalog]
    B -->|No| D{Package in JFrog Platform Stored Packages?}
    D -->|Yes| E[Get latest version from Stored Packages]
    D -->|No| F[Package not found — stop]
    C --> G{Latest version in JFrog Platform?}
    E --> G
    G -->|Yes| H[Safe — download from JFrog Platform]
    G -->|No| I{Curation entitled?}
    I -->|Yes| J[Check curation policy via API]
    I -->|No| K[Download via remote repo]
    J -->|200 Allowed| K
    J -->|403 Blocked| M[Report curation blocked — stop]

Parallelization opportunities

Several steps in this workflow are independent and can run in parallel to reduce total latency:

When issuing parallel Shell calls, each jf api call authenticates independently against the active jf config server; no shell state needs to be passed between calls.

Step 1: Find the package

Search the Public Catalog first via OneModel GraphQL, then fall back to Stored Packages if not found.

Execute the query through jf api as described in ../jfrog/references/onemodel-graphql.md; refer to ../jfrog/references/onemodel-query-examples.md for concrete query shapes.

When package type is known (e.g. npm, maven, pypi), use publicPackages.getPackage(type:, name:) (see Get a public package). Include the latestVersion { version } selection set — latestVersion is an object, not a scalar.

When type is unknown, use publicPackages.searchPackages with nameContains (see Search public packages). Add type: when the user narrows the ecosystem.

If multiple results with different type values, ask the user which package type they mean.

Step 2: Determine latest version

Source Version field
Public Catalog latestVersion.version (object selection required)
JFrog Platform Stored Packages latestVersionName on StoredPackage, or highest entry from versionsConnection

Step 3: Check if package + latest version exists in JFrog Platform

Query stored package versions using storedPackages.searchPackageVersions with a hasPackageWith filter (see ../jfrog/references/onemodel-query-examples.mdSearch stored package versions). Add a version filter for the specific version from Step 2, and request locationsConnection to get repository details (repositoryKey, repositoryType, leadArtifactPath).

Execute the query through jf api (see ../jfrog/references/onemodel-graphql.md for the invocation pattern).

Step 4: Download from JFrog Platform

Use the location info from Step 3. Binary artifact downloads go through jf rt dlnot jf api. jf api is the unified entry point for the JFrog REST APIs (metadata, admin, curation, etc.) and does not expose the -L / -o flags needed to stream binary content through a redirect chain.

<target> must be a full file path (e.g. ./downloads/lodash-4.18.1.tgz), not a bare directory. jf rt dl --flat treats the target as a file name; passing a directory causes a misleading “open path: is a directory” error.

repositoryType Strategy
local or federated jf rt dl "<repositoryKey>/<leadArtifactPath>" <target-file> --flat
remote jf rt dl against the base remote repo (strip any trailing -cache) — it transparently triggers the remote fetch when the artifact is not yet cached

local / federated / remote download:

jf rt dl "<baseRepoKey>/<leadArtifactPath>" <target-file> --flat

Resolving the remote repo key: The repositoryKey returned by OneModel for remote locations often already ends in -cache (e.g. devNPM-remote-cache). jf rt dl needs the base remote repo name (without -cache). Strip the -cache suffix when present (e.g. devNPM-remote-cachedevNPM-remote). If the key does not end in -cache, use it as-is.

See the Protocol endpoints table below for the package-type-specific path format inside the repo.

Step 5: Check curation entitlement

jf api /artifactory/api/system/version \
  | jq '.addons | index("curation") != null'

Step 6a: Check curation policy and download

When curation is entitled, use the Xray curation API to check whether the package version is allowed across all repositories before downloading.

RESPONSE_FILE="/tmp/curation-status-$$.json"
PAYLOAD_FILE="/tmp/curation-payload-$$.json"
STDERR_FILE="/tmp/curation-err-$$.log"

jq -n \
  --arg type    "<TYPE>"    \
  --arg name    "<NAME>"    \
  --arg version "<VERSION>" \
  '{packageType:$type, packageName:$name, packageVersion:$version}' \
  > "$PAYLOAD_FILE"

set +e
jf api /xray/api/v1/curation/package_status/all_repos \
  -X POST -H "Content-Type: application/json" \
  --input "$PAYLOAD_FILE" \
  > "$RESPONSE_FILE" 2> "$STDERR_FILE"
RC=$?
set -e
echo "RC=$RC"; echo "$RESPONSE_FILE"

Supported packageType values: npm, pypi, maven, go, nuget, docker, gradle.

Interpreting the result with jf api: unlike plain curl, jf api surfaces the HTTP result through its exit code and a "<hh:mm:ss> [Warn] ... returned 4xx/5xx" line on stderr (not a %{http_code} suffix in stdout). The response body is always written to stdout. Parse both:

if [ "$RC" -eq 0 ]; then
  echo "Package is allowed by curation."
elif grep -q 'returned 403' "$STDERR_FILE"; then
  echo "Blocked by curation policy:"
  cat "$RESPONSE_FILE"
else
  echo "Curation check failed (rc=$RC):"
  cat "$STDERR_FILE"
fi

Evaluate the outcome:

Step 6b: Download without curation

When curation is not entitled and the package is not in the JFrog Platform, download directly through a remote repo.

  1. Find a remote repo of the right package type:

    jf api \
      "/artifactory/api/repositories?type=remote&packageType=<TYPE>" \
      | jq '.[].key'
    
  2. Download — use jf rt dl against the base remote repo (without -cache); it handles both cached and uncached artifacts:

    jf rt dl "<repo>/<artifact-path>" <target-file> --flat
    

Artifact paths by package type

Use these path patterns when leadArtifactPath is not available from OneModel. The leading <repo>/ is the base repo key you pass to jf rt dl.

Type jf rt dl target pattern
npm <repo>/<pkg>/-/<pkg>-<version>.tgz
pypi <repo>/<pkg>/<version>/<pkg>-<version>.tar.gz
maven <repo>/<group-path>/<artifact>/<version>/<artifact>-<version>.jar
go <repo>/<module>/@v/<version>.zip

Gotchas

Skill frontmatter

metadata: {"role"=>"workflow"}