Source 8c0bfa77cda996e83b198a31
ASecurityCheck/download a package (npm, Maven, PyPI, Go...) via JFrog — safe, allowed, curated? Or: package op fails/blocked (ETARGET, 403, blocked by curation policy, missing version, waiver) — root cause it. Checks the JFrog Public Catalog and stored packages for a version, interprets catalog security signals, and downloads through Artifactory (JFrog Platform locations, remote cache, curation-aware package managers, or repo proxy). Do NOT use for pure CVE or vulnerability lookups (e.g. "details on C...
- 2 stars
- 0 votes
- 0 copies
- 0 views
- Added November 7, 2026
Works with
Security analysis
100/100Pro scans all 4 files and shows the line behind each finding
npx -y skills add gutsofpratik/OpenClaude-Skills --skill source-8c0bfa77cda996e83b198a31 --agent claude-codeAre you the author of Source 8c0bfa77cda996e83b198a31?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/gutsofpratik-source-8c0bfa77cda996e83b198a31)---
name: jfrog-package-curation
description: >-
Check/download a package (npm, Maven, PyPI, Go...) via JFrog — safe,
allowed, curated? Or: package op fails/blocked (ETARGET, 403, blocked by
curation policy, missing version, waiver) — root cause it. Checks the JFrog Public
Catalog and stored packages for a version, interprets catalog security
signals, and downloads through Artifactory (JFrog Platform locations,
remote cache, curation-aware package managers, or repo proxy). 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. Do NOT use for installing, listing, or approving MCP
servers/tools (even when named like a package, e.g. `@scope/pkg`) — that's
`jfrog-mcp-management`.
compatibility: >-
"Check & download" needs only the JFrog CLI (`jf`). "Troubleshoot a
failure" needs the JFrog MCP curation tools (`jfs_curation_*`) — `jf` CLI
alone does not cover it; if unavailable, that section cannot run (see its
Preflight). Its reference file is local; outbound HTTPS is optional there
too (on-demand WebFetch of JFrog docs) — without it, it runs on the MCP
tools and local reference, degraded.
metadata:
role: workflow
---
# JFrog Package Curation
## Prerequisites
- Read `../jfrog/SKILL.md` for JFrog Platform concepts, domain model, CLI setup, and API patterns.
- **OneModel shapes drift by server version.** Before inventing GraphQL fields or `where` filters, read `../jfrog/references/onemodel-graphql.md` (schema fetch workflow) and `../jfrog/references/onemodel-query-examples.md` (**Public packages**, **Stored packages**). Regenerate or verify queries against `GET "$JFROG_URL/onemodel/api/v1/supergraph/schema"` when examples fail validation.
## Which path do I need?
- **Package hasn't been checked or downloaded yet** (is it safe / allowed / curated? download it) → go to **Check & download** below.
- **A package operation already failed** — `ETARGET` (npm), "no matching distribution" (pip), `NU1102` (NuGet), a version missing/stripped from the registry, `403` / "blocked by curation policy", or an approved waiver not taking effect → go to **Troubleshoot a failure** below.
---
# Troubleshoot a failure
> **Requires the JFrog MCP curation tools** (`jfs_curation_*`); no `jf` CLI
> equivalent is used in this path. See the skill's `compatibility` note.
Root-causing a curation-attributable failure (`ETARGET`, `403`, a version
missing from the index, a waiver not taking effect) is a multi-step
investigation — preflight checks, the two-gate model, symptom classification,
then a full RCA procedure. It doesn't belong inline here since a plain
download never touches it. **Open
[references/rca-procedure.md](references/rca-procedure.md) and follow it from
the top** — one file, covering preflight, the #1 RULE, the two-gate model,
symptom classification (Step 0), and the RCA itself (Steps 1–4, worked
examples, gotchas).
---
# Check & download
## Workflow overview
```mermaid
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 — see Troubleshoot a failure]
```
### Parallelization opportunities
Several steps in this workflow are independent and can run in parallel to
reduce total latency:
- **Step 1 + Step 1 fallback**: When package type is known, query both the
Public Catalog (`getPackage`) and Stored Packages (`getPackage`) in
parallel. Use whichever returns data; if the Public Catalog returns a hit,
prefer its `latestVersion` for Step 2.
- **Step 3 + Step 5**: After determining the version, query stored package
versions (JFrog Platform check) and curation entitlement
(`/api/system/version`) in parallel. Both are independent reads — the
curation result is needed immediately if the JFrog Platform check returns
empty.
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.
- **Found** → note `type` and `latestVersion.version`. Proceed to Step 2.
- **Not found** → the package may be 1st/2nd party. Search **Stored Packages**
using `storedPackages.searchPackages` or `storedPackages.getPackage` (see
*Stored packages domain* in `onemodel-query-examples.md`). Prefer
filtering by `type` when known; if not, use `nameContains` alone.
- **Found** → note `type` and `latestVersionName` (or derive a version from
`versionsConnection`). Proceed to Step 2.
- **Not found in either** → report "package not found" and stop.
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.md`
→ *Search 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).
- **Found with locations** → package is in the JFrog Platform. Report as **safe to
download**. Proceed to Step 4.
- **Not found** → proceed to Step 5.
## Step 4: Download from JFrog Platform
Use the location info from Step 3. Binary artifact downloads go through
`jf rt dl` — **not** `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:**
```bash
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-cache` → `devNPM-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
```bash
jf api /artifactory/api/system/version \
| jq '.addons | index("curation") != null'
```
- `true` → curation is entitled. Proceed to Step 6a.
- `false` → curation not available. Proceed to Step 6b.
## 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.
```bash
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:
```bash
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:**
- **exit 0** → package is **allowed** by curation policy. Proceed to
download via a remote repo (same as Step 6b).
- **`returned 403` on stderr** → package is **blocked** by a curation
policy. The response body explains which policy rule blocked it. Report
the block reason to the user and stop — do not attempt to download. For
root-cause detail beyond the block reason (why this version specifically,
whether a waiver applies, what to use instead), see **Troubleshoot a
failure** above.
- **Any other non-zero exit** → treat as an operational failure (auth, DNS,
endpoint disabled) and report.
## 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:
```bash
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:
```bash
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
- **Binary downloads vs. `jf api`**: `jf api` is for REST APIs, not binary
content. It does not follow redirects transparently into a binary payload
and does not expose `-L` / `-o`. Always use `jf rt dl` (against the base
remote repo, not the `-cache` one) for the actual artifact download.
- **`jf rt dl` and uncached remotes**: `jf rt dl "<remote>/<path>"` —
targeting the **base** remote repo rather than `<remote>-cache/<path>` —
transparently triggers the remote fetch and caches the artifact. Do not
try to pre-query the proxy via `jf api`.
- **`jf rt dl --flat` target must be a file path**: When downloading a
single artifact, pass a full output **file** path (e.g.
`./downloads/lodash-4.18.1.tgz`), not a directory. The CLI opens the target
path as a file; a directory causes a cryptic "open path: is a directory"
error that retries four times before failing. Derive the filename from
`leadArtifactPath` (take the segment after the last `/`).
- **Package type detection**: If the user doesn't specify the package type,
the Public Catalog search by name alone may return multiple types. Ask the
user to disambiguate before proceeding.
- **Curation endpoint lives under Xray**: use
`/xray/api/v1/curation/package_status/all_repos` (via `jf api`). Do not
prefix it with `/artifactory`.
- **Curation result discrimination with `jf api`**: the 200/403 signal comes
from `jf api`'s **exit code** plus a `returned NNN` line on **stderr**,
not from a `%{http_code}` appended to stdout. Capture stderr to a file
(`2> "$STDERR_FILE"`) and branch on `RC` + `grep 'returned 403'` as shown
in Step 6a.
- **Curation API package type values**: Must be lowercase and match one of
`npm`, `pypi`, `maven`, `go`, `nuget`, `docker`, `gradle`. Other values
will return an error.
Files in this skill
- PROVENANCE.json
- SKILL.md
- UPSTREAM-LICENSE.txt
- references/rca-procedure.md
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.