> ## Documentation Index
> Fetch the complete documentation index at: https://docs.endorlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Package Firewall

> Learn how to block known malicious packages and control package installations for npm and pip by integrating JFrog Artifactory with Package Firewall.

export const YamlTable = ({children, data: propData, content}) => {
  const KV_RE = /^([A-Za-z][A-Za-z0-9_()/#\s-]+?):\s*(.+)$/;
  const INLINE_MD_RE = /(\[([^\]]+)\]\(([^)]+)\))|(`([^`]+)`)|(\*\*([^*]+)\*\*)|(\*([^*]+)\*)/g;
  const YES_RE = /^-yes-$/i;
  const NO_RE = /^-no-$/i;
  const LIMITED_RE = /^-(limited|partial)-$/i;
  const NA_RE = /^-(na|none)-$/i;
  const NA2_RE = /^-na2-$/i;
  const SIMPLE_TAG_RE = /(<br\s*\/?>)|(<p\s*\/?>)|(-note-)|(-warning-)/gi;
  const tryParseKV = trimmed => {
    const m = KV_RE.exec(trimmed);
    return m ? {
      key: m[1],
      value: m[2].trim()
    } : null;
  };
  const registerKey = (key, seenKeys, orderedKeys) => {
    if (!seenKeys.has(key)) {
      orderedKeys.push(key);
      seenKeys.add(key);
    }
  };
  const flushEntry = (currentEntry, entries) => {
    if (Object.keys(currentEntry).length > 0) entries.push(currentEntry);
  };
  const parseDashPrefixed = (lines, entries, orderedKeys, seenKeys) => {
    let currentEntry = {};
    let inEntry = false;
    for (const line of lines) {
      const trimmed = line.trim();
      if (trimmed.startsWith('- ')) {
        if (inEntry) entries.push(currentEntry);
        currentEntry = {};
        inEntry = true;
        const kv = tryParseKV(trimmed.substring(2).trim());
        if (kv) {
          registerKey(kv.key, seenKeys, orderedKeys);
          currentEntry[kv.key] = kv.value;
        }
      } else if (inEntry && trimmed !== '') {
        const kv = tryParseKV(trimmed);
        if (kv) {
          registerKey(kv.key, seenKeys, orderedKeys);
          currentEntry[kv.key] = kv.value;
        }
      }
    }
    flushEntry(currentEntry, entries);
  };
  const parseBlankSeparated = (lines, entries, orderedKeys, seenKeys) => {
    let currentEntry = {};
    let inEntry = false;
    for (const line of lines) {
      const trimmed = line.trim();
      if (trimmed === '') {
        if (inEntry) {
          flushEntry(currentEntry, entries);
          currentEntry = {};
          inEntry = false;
        }
        continue;
      }
      const kv = tryParseKV(trimmed);
      if (!kv) continue;
      const isNewEntry = !line.startsWith(' ') && !line.startsWith('\t');
      if (isNewEntry && inEntry && Object.keys(currentEntry).length > 0) {
        entries.push(currentEntry);
        currentEntry = {};
      }
      registerKey(kv.key, seenKeys, orderedKeys);
      currentEntry[kv.key] = kv.value;
      inEntry = true;
    }
    flushEntry(currentEntry, entries);
  };
  const normalizeEntries = (entries, orderedKeys) => entries.map(entry => {
    const filled = {};
    for (const key of orderedKeys) filled[key] = entry[key] || '';
    return filled;
  });
  const parseYamlTableContent = contentStr => {
    if (!contentStr) return [];
    const entries = [];
    const orderedKeys = [];
    const seenKeys = new Set();
    const lines = contentStr.split('\n');
    if (lines.some(line => line.trim().startsWith('- '))) {
      parseDashPrefixed(lines, entries, orderedKeys, seenKeys);
    } else {
      parseBlankSeparated(lines, entries, orderedKeys, seenKeys);
    }
    return normalizeEntries(entries, orderedKeys);
  };
  const processText = text => {
    if (!text) return text;
    const parts = [];
    let keyIndex = 0;
    let lastIndex = 0;
    let match;
    while ((match = INLINE_MD_RE.exec(text)) !== null) {
      if (match.index > lastIndex) parts.push(text.slice(lastIndex, match.index));
      if (match[1]) {
        parts.push(<a key={keyIndex++} href={match[3]}>{match[2]}</a>);
      } else if (match[4]) {
        parts.push(<code key={keyIndex++}>{match[5]}</code>);
      } else if (match[6]) {
        parts.push(<strong key={keyIndex++}>{match[7]}</strong>);
      } else if (match[8]) {
        parts.push(<em key={keyIndex++}>{match[9]}</em>);
      }
      lastIndex = match.index + match[0].length;
    }
    if (lastIndex < text.length) parts.push(text.slice(lastIndex));
    if (parts.length === 0) return text;
    const keyRef = {
      current: keyIndex
    };
    return expandHtmlTags(parts, keyRef);
  };
  const processBadges = text => {
    if (!text || typeof text !== 'string') return text;
    if (YES_RE.test(text)) return <span className="yt-badge-yes" role="img" aria-label="Supported" title="Supported">✓</span>;
    if (NO_RE.test(text)) return <span className="yt-badge-no" role="img" aria-label="Not supported" title="Not supported">✗</span>;
    if (LIMITED_RE.test(text)) return <span className="yt-badge-limited" role="img" aria-label="Partially supported" title="Partially supported">◐</span>;
    if (NA_RE.test(text) || NA2_RE.test(text)) return <span className="yt-sr-only" title="Not applicable">Not applicable</span>;
    return processText(text);
  };
  const cellClassName = text => {
    if (!text || typeof text !== 'string') return undefined;
    if (NA_RE.test(text)) return 'yt-cell-na';
    if (NA2_RE.test(text)) return 'yt-cell-na2';
    return undefined;
  };
  const expandSimpleTags = (str, keyRef) => {
    const result = [];
    let last = 0;
    SIMPLE_TAG_RE.lastIndex = 0;
    let m;
    while ((m = SIMPLE_TAG_RE.exec(str)) !== null) {
      if (m.index > last) result.push(str.slice(last, m.index));
      if (m[1]) {
        result.push(<br key={keyRef.current++} />);
      } else if (m[2]) {
        result.push(<br key={keyRef.current++} />, <br key={keyRef.current++} />);
      } else if (m[3]) {
        result.push(<span key={keyRef.current++} className="yt-badge-note" style={{
          fontWeight: 600
        }}>Note: </span>);
      } else if (m[4]) {
        result.push(<span key={keyRef.current++} className="yt-badge-warning" style={{
          fontWeight: 600
        }}>Warning: </span>);
      }
      last = m.index + m[0].length;
    }
    if (last < str.length) result.push(str.slice(last));
    return result;
  };
  const expandHtmlTags = (chunks, keyRef) => {
    const out = [];
    for (const chunk of chunks) {
      if (typeof chunk === 'string') {
        out.push(...expandSimpleTags(chunk, keyRef));
      } else {
        out.push(chunk);
      }
    }
    return out;
  };
  const extractText = node => {
    if (node === null || node === undefined) return '';
    if (typeof node === 'string') return node;
    if (typeof node === 'number') return String(node);
    if (typeof node === 'boolean') return '';
    if (Array.isArray(node)) return node.map(extractText).join('');
    if (node && typeof node === 'object' && node.type) {
      const props = node.props || ({});
      if (typeof props.children === 'string') return props.children;
      if (props.children) return extractText(props.children);
      return '';
    }
    return String(node || '');
  };
  const [mounted, setMounted] = useState(false);
  useEffect(() => {
    setMounted(true);
  }, []);
  const data = useMemo(() => {
    if (propData) return propData;
    if (content && typeof content === 'string') return parseYamlTableContent(content);
    if (!children) return [];
    if (typeof children === 'string') return parseYamlTableContent(children);
    const childrenArray = Array.isArray(children) ? children : [children];
    return parseYamlTableContent(childrenArray.map(extractText).join('').trim());
  }, [children, propData, content]);
  const columns = useMemo(() => {
    if (!data || data.length === 0) return [];
    const firstRow = data[0];
    if (!firstRow || typeof firstRow !== 'object') return [];
    return Object.keys(firstRow);
  }, [data]);
  if (!mounted) return null;
  if (!data || data.length === 0) return null;
  const rowKey = row => columns.map(c => row[c] || '').join('|');
  return <table>
      <thead>
        <tr>
          {columns.map(col => <th key={col}>{col.replaceAll('_', ' ')}</th>)}
        </tr>
      </thead>
      <tbody>
        {data.map(row => <tr key={rowKey(row)}>
            {columns.map(col => <td key={col} className={cellClassName(row[col])}>{processBadges(row[col])}</td>)}
          </tr>)}
      </tbody>
    </table>;
};

The Package Firewall is Endor Labs' SaaS solution that offers real-time protection against malicious packages during software installations. It safeguards your software supply chain by preventing malicious packages from reaching your developers. Positioned between package managers and public package registries, it automatically blocks the installation of packages containing malware while allowing safe packages to install normally.

Security teams often discover malware only after it enters your environment, forcing reactive cleanup. The Package Firewall closes that gap by intercepting every package installation request before it completes. Integrate Artifactory with the Package Firewall so Artifactory uses Endor Labs as the remote for public registry traffic. The Package Firewall blocks any malicious package instantly and returns an error, so it never reaches your environment. Legitimate packages pass through unchanged, keeping your developers productive and your pipeline secure.

You can configure a Package Firewall policy to customize which packages the firewall flags and how it responds. The firewall can flag packages based on malware, restricted licenses, or minimum age threshold violations. For each criteria, you can choose between two responses:

* **Warn**: Records the event and allows the package installation without interrupting your CI pipeline.
* **Block**: Prevents the package installation entirely and returns an error.

You can also define exceptions to allow specific packages through regardless of policy. See [Package Firewall policy](/platform-administration/policies/package-firewall-policies) to learn more.

The Package Firewall checks every package in the dependency tree individually, including transitive dependencies. If it flags any dependency, the installation is blocked. Configured Package Firewall policies apply to transitive dependencies as well.

The Package Firewall supports any package manager that uses the npm or PyPI registry, such as npm and pip.

<Note>
  **License requirement**

  Ensure that you have the **Package Firewall** license. See [Licenses](/introduction/licenses) for more information.
</Note>

## How it works

The Package Firewall inspects package requests before serving them. When a developer or CI pipeline requests a package to download, the request goes to your private registry, such as JFrog Artifactory.

Configure Artifactory to use the Package Firewall URL as its remote source instead of upstream package registries. This setup ensures that the firewall inspects every package request before downloading the package.

The following steps describe how Package Firewall intercepts each package installation request and its response.

1. **Route traffic through the Package Firewall**: Artifactory forwards each request to the Package Firewall so that the firewall evaluates every package before serving it to the developer or pipeline.

2. **Authenticate and control access**: Requests to the Package Firewall require an Endor Labs API key with the appropriate role and license.

3. **Evaluate each package request**: For each package request, the Package Firewall parses the ecosystem, package name, and version, and checks it against the Endor Labs' malware database. If a Package Firewall policy is configured, the firewall also evaluates restricted licenses, minimum package age, and exceptions before deciding on the action.

4. **Firewall decision**: Based on the malware check and policy conditions, the Package Firewall either blocks package installations or allows it with a warning log. If no criteria matches, the package is allowed and no log is recorded.

   * The Package Firewall blocks the request and Artifactory returns **HTTP 404** if the package is found in Endor Labs' malware database, or if a policy condition matches with **Block**. The Package Firewall records an event with the package, version, and reason.
   * The Package Firewall allows the installation and redirects the client with **HTTP 307** if a policy condition matches with **Warn**. The Package Firewall records a warning event with the package, version, and reason.
   * The Package Firewall allows the installation and redirects the client with **HTTP 307** if no malware is detected. The client then downloads the package directly from the public registry through this redirect.

5. **View Package Firewall logs**: Package Firewall logs the actions the firewall takes on package installation requests, which is helpful for debugging and compliance. The logs include details such as the package, version, time of the event, and the reason the package was blocked or allowed with a warning. If no criteria matches, the package is allowed and no log is recorded.

## Prerequisites

* You must have the **Package Firewall** license.
* JFrog Artifactory with permission to create remote repositories and configure credentials.
* Download and install endorctl. See [Install and configure endorctl](/developers-api/cli/install-and-configure) for more information.

## Configure the Package Firewall

Complete the following steps to integrate JFrog Artifactory with the Endor Labs Package Firewall:

1. [Create an API key for the Package Firewall](#create-an-api-key-for-the-package-firewall).
2. [Configure JFrog Artifactory](#configure-jfrog-artifactory).
3. [Verify that the Package Firewall is blocking malware](#verify-that-the-package-firewall-is-blocking-malware).
4. [Query Package Firewall logs](#query-package-firewall-logs).

### Create an API key for the Package Firewall

You must create an API key with the `SYSTEM_ROLE_PACKAGE_FIREWALL` role. JFrog Artifactory uses this key to authenticate to the Package Firewall. Run the following command and replace:

* `<namespace>` with your namespace.
* `<API key name>` with the name of the API key for the Package Firewall use case.
* `<YYYY-MM-DDTHH:MM:SSZ>` with the API key expiration in ISO 8601 UTC format, for example `2026-12-31T23:59:59Z`.

```bash theme={null}
export NAMESPACE="<namespace>"
export KEY_NAME="<API key name>"

endorctl api create -r APIKey -n "$NAMESPACE" --data '{
  "meta": { "name": "'"$KEY_NAME"'" },
  "spec": {
    "permissions": { "roles": ["SYSTEM_ROLE_PACKAGE_FIREWALL"] },
    "expiration_time": "<YYYY-MM-DDTHH:MM:SSZ>"
  },
  "propagate": true
}'
```

From the response, save the username and password in a secure location. Use these credentials when you configure the JFrog Artifactory remote repository.

* **Username:** `spec.key`
* **Password:** `spec.secret`

### Configure JFrog Artifactory

Configure a remote repository in JFrog Artifactory so package traffic goes through the Package Firewall. Use the steps below for either npm or PyPI.

<AccordionGroup>
  <Accordion title="Configure JFrog Artifactory for an npm remote repository">
    1. [Log in to JFrog Artifactory](https://my.jfrog.com/login/).

    2. Select **Administration** > **Repositories** from the left sidebar.

    3. Click **Create a Repository** and select **Remote**.

    4. Select **npm** as the package type.

           <img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/integrations/package-firewall/package-firewall/npm-1.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=eeee21881bc6a0637d39935f0abb5167" alt="Select npm" width="2262" height="1040" data-path="images/integrations/package-firewall/package-firewall/npm-1.webp" />

    5. Enter a **Repository Key**, for example `endor-firewall-npm`.

    6. Enter the repository URL: `https://factory.endorlabs.com/v1/namespaces/<namespace>/firewall/npm/`. Replace `<namespace>` with your Endor Labs namespace.

    7. Enter the **User Name** and **Password** you saved when creating the API key.

    8. Click **Create Remote Repository**.

           <img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/integrations/package-firewall/package-firewall/npm-2.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=e9c0a8be28458a17e749fc23bbfdf4c6" alt="Configure npm" width="3016" height="1484" data-path="images/integrations/package-firewall/package-firewall/npm-2.webp" />
  </Accordion>

  <Accordion title="Configure JFrog Artifactory for a PyPI remote repository">
    1. [Log in to JFrog Artifactory](https://my.jfrog.com/login/).

    2. Select **Administration** > **Repositories** from the left sidebar.

    3. Click **Create a Repository** and select **Remote**.

    4. Select **PyPI** as the package type.

           <img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/integrations/package-firewall/package-firewall/pypi-1.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=4caf7568eb7233a449c56007ff5072bf" alt="Select PyPI" width="2262" height="1040" data-path="images/integrations/package-firewall/package-firewall/pypi-1.webp" />

    5. Enter a **Repository Key**, for example `endor-firewall-pypi`.

    6. Enter the repository URL: `https://factory.endorlabs.com/v1/namespaces/<namespace>/firewall/pypi/`. Replace `<namespace>` with your Endor Labs namespace.

    7. Enter the **User Name** and **Password** you saved when creating the API key.

    8. In **PyPI Settings**, set **Registry URL** to the same URL you entered in step 6.

    9. Click **Create Remote Repository**.

           <img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/integrations/package-firewall/package-firewall/pypi-2.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=7db5aafb88c73c3cbe9595f0c920018a" alt="Configure PyPI" width="3016" height="1504" data-path="images/integrations/package-firewall/package-firewall/pypi-2.webp" />
  </Accordion>
</AccordionGroup>

#### Local setup for developers

Update your package manager to use Artifactory as its source, routing all installs through the Package Firewall instead of the public registry. Once you create the Artifactory remote:

1. Navigate to **Administration** > **Repositories**.
2. Click the three vertical dots next to the repository you configured and select **Set Me Up**.
3. Enter **Your JFrog account password** if prompted.
4. Click **Generate Token & Create Instructions**.
5. Follow the instructions to configure your local machine, `.npmrc` or `pip.conf`, based on your package type. Refer to [JFrog Artifactory documentation](https://docs.jfrog.com/artifactory/docs/use-artifactory-set-me-up-for-configuring-package-manager-clients) for more information.
6. Run the following command to verify that your local registry is pointing to the Artifactory remote. Ensure the output matches the Artifactory remote URL you configured.

   * For npm packages, run `npm config get registry`.
   * For PyPI packages, run `pip3 config list | grep index-url`.

   <img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/integrations/package-firewall/package-firewall/local-setup.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=19f1bd9cb18c0c4ee199325bf1e350e2" alt="Local setup for developers" width="2261" height="1179" data-path="images/integrations/package-firewall/package-firewall/local-setup.webp" />

### Verify that the Package Firewall is blocking malware

After configuring `.npmrc` or `pip.conf`, try installing a package that Endor Labs has classified as malware. The Package Firewall blocks the installation and Artifactory returns a `404`.

The following are examples of packages classified as malware in the Endor Labs.

<AccordionGroup>
  <Accordion title="npm">
    Run the following command to test the Package Firewall with npm.

    ```bash theme={null}
    npm install endor-firewall-test@1.0.0
    ```

    When the Package Firewall blocks the package, the output looks similar to the following. The `E404` error code and `404 Not Found` response confirm that the firewall blocked the package.

    ```bash theme={null}
    npm error code E404
    npm error 404 Not Found - GET https://johndoe.jfrog.io/artifactory/api/npm/johndoe/endor-firewall-test/-/endor-firewall-test-1.0.0.tgz
    npm error 404
    npm error 404  The requested resource 'endor-firewall-test@https://johndoe.jfrog.io/artifactory/api/npm/johndoe/endor-firewall-test/-/endor-firewall-test-1.0.0.tgz' could not be found or you do not have permission to access it.
    npm error 404
    npm error 404 Note that you can also install from a
    npm error 404 tarball, folder, http url, or git url.
    ```
  </Accordion>

  <Accordion title="pip">
    Run the following command to test the Package Firewall with pip.

    ```bash theme={null}
    pip install endor-firewall-test==1.0.0
    ```

    When the Package Firewall blocks the package, the output looks similar to the following. The `E404` error code and `404 Not Found` response confirm that the firewall blocked the package.

    ```bash theme={null}
    Defaulting to user installation because normal site-packages is not writeable
    Looking in indexes: https://johndoe.jfrog.io/artifactory/api/pypi/johndoe/simple
    Collecting endor-firewall-test==1.0.0
    ERROR: HTTP error 404 while getting https://johndoe.jfrog.io/artifactory/api/pypi/johndoe/packages/packages/61/05/6e99035fec6c7e407fffc052a0060495f6a2fcae2143db3239c7399d5b6e/endor_firewall_test-1.0.0-py3-none-any.whl#sha256=4df734939186708c595e72e50f5d31296d2ea9e54d5a0afc9e69d4e7d6f0d4b9 (from https://johndoe.jfrog.io/artifactory/api/pypi/johndoe/simple/endor-firewall-test/) (requires-python:>=3.7)

    ERROR: Could not install requirement endor-firewall-test==1.0.0 from https://johndoe.io/artifactory/api/pypi/johndoe/packages/packages/61/05/6e99035fec6c7e407fffc052a0060495f6a2fcae2143db3239c7399d5b6e/endor_firewall_test-1.0.0-py3-none-any.whl#sha256=4df734939186708c595e72e50f5d31296d2ea9e54d5a0afc9e69d4e7d6f0d4b9 because of HTTP error 404 Client Error:  for url: https://johndoe.jfrog.io/artifactory/api/pypi/johndoe/packages/packages/61/05/6e99035fec6c7e407fffc052a0060495f6a2fcae2143db3239c7399d5b6e/endor_firewall_test-1.0.0-py3-none-any.whl for URL https://johndoe.jfrog.io/artifactory/api/pypi/johndoe/packages/packages/61/05/6e99035fec6c7e407fffc052a0060495f6a2fcae2143db3239c7399d5b6e/endor_firewall_test-1.0.0-py3-none-any.whl#sha256=4df734939186708c595e72e50f5d31296d2ea9e54d5a0afc9e69d4e7d6f0d4b9 (from https://johndoe.jfrog.io/artifactory/api/pypi/johndoe/simple/endor-firewall-test/) (requires-python:>=3.7)
    ```
  </Accordion>
</AccordionGroup>

### View Package Firewall logs

To view Package Firewall logs in the Endor Labs user interface:

1. Select **Findings** from the left sidebar.

2. Select **Package Firewall**.
   <draft>
     <img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/integrations/package-firewall/package-firewall/pkg-firewall.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=49f64fdfc7a60d3547df87c7fa7c05be" alt="Package Firewall" width="2658" height="1264" data-path="images/integrations/package-firewall/package-firewall/pkg-firewall.webp" />
   </draft>

3. Select an event to view the following details:

   * **Info**: Package name, package version, API key, remote address, request URL, action taken, the reason the event was flagged, and when the event occurred.

   For malware events, you can also view the following:

   * **Risk Details**: Explanation of why the package was flagged and remediation guidance.
   * **Metadata**: Ecosystem, package release date, advisory published date, CWE ID, and OSV ID when available.
   * **Malware Info**: Malware detection record in raw JSON format.

   For minimum package age events, you can also view the package age in hours.

   For restricted license events, you can also view the detected package license.

<draft>
  <img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/integrations/package-firewall/package-firewall/pkg-firewall-log.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=4861aa5cf1e5bfb13f3248198ae3558a" alt="Package Firewall log" width="2762" height="1462" data-path="images/integrations/package-firewall/package-firewall/pkg-firewall-log.webp" />
</draft>

### Filter Package Firewall logs

Use filters to narrow Package Firewall logs by ecosystem, action, rule reason, or time.

1. Select **Findings** from the left sidebar.
2. Select **Package Firewall**.
3. Toggle the filter panel to show the filters.
4. Set any of the following filters to narrow the log list.

   * **Ecosystem** - Filter logs by their package ecosystem.
   * **Action**: Filter logs by the action taken on the package installation, either **Warning** or **Blocked**.
   * **Reason**: Filter logs by why the package was flagged, which can be **Malware detected**, **Minimum package age not met**, or **Restricted license**.
   * **All Time** - Filter logs by when the event was recorded. You can select **All Time**, **Last Day**, **Last Week**, **Last Month**, **Last 60 Days**, **Last 90 Days**, or a custom range.

   You can use the same filters to query logs through `endorctl`. See [Query Package Firewall logs using endorctl](#query-package-firewall-logs-using-endorctl).

### Query Package Firewall logs using endorctl

The Package Firewall logs record every action the firewall takes on package installation requests. You can view them by querying the `endorctl` API.

* To list all Package Firewall logs in your namespace, run the following command. Replace `<namespace>` with your namespace.

  ```bash theme={null}
  endorctl api list -r PackageFirewallLog -n <namespace>
  ```

* To list logs only for a specific ecosystem, add a filter.

  ```bash theme={null}
  endorctl api list -r PackageFirewallLog -n <namespace> --filter 'spec.ecosystem==<ecosystem_variable>'
  ```

  Replace:

  * `<ecosystem_variable>` with `ECOSYSTEM_NPM` for npm and `ECOSYSTEM_PYPI` for PyPI.
  * `<namespace>` with your namespace.

* To list logs for a specific package in an ecosystem, use a filter with `spec.ecosystem`, `spec.package_name`, and `spec.package_version`.

  ```bash theme={null}
  endorctl api list -r PackageFirewallLog -n <namespace> --filter 'spec.ecosystem==<ecosystem_variable> and spec.package_name=="<package_name>" and spec.package_version=="<package_version>"'
  ```

  Replace:

  * `<namespace>` with your namespace.
  * `<ecosystem_variable>` with `ECOSYSTEM_NPM` for npm and `ECOSYSTEM_PYPI` for PyPI.
  * `<package_name>` with the package name you want to query.
  * `<package_version>` with the package version you want to query.

You can use a combination of filters to narrow your query.

<YamlTable>
  {`

    - Filter: \`spec.ecosystem\`
    Supported values: \`ECOSYSTEM_NPM\`, \`ECOSYSTEM_PYPI\`
    Description: Ecosystem of the package.
    - Filter: \`spec.package_name\`
    Supported values: String
    Description: Name of the package in the ecosystem's normal form.
    - Filter: \`spec.package_version\`
    Supported values: String
    Description: Version of the package resolved for the request, when available.
    - Filter: \`spec.action\`
    Supported values: \`PACKAGE_FIREWALL_ACTION_WARN\`, \`PACKAGE_FIREWALL_ACTION_BLOCK\`
    Description: Action the firewall took on the request.
    - Filter: \`spec.reason\`
    Supported values: \`PACKAGE_FIREWALL_REASON_MALWARE_DETECTED\`, \`PACKAGE_FIREWALL_REASON_MIN_AGE_NOT_MET\`, \`PACKAGE_FIREWALL_REASON_RESTRICTED_LICENSE\`
    Description: Reason the package was flagged.
    - Filter: \`spec.package_age_hours\`
    Supported values: Integer
    Description: Number of hours since the package was published. Use when the reason is set to \`PACKAGE_FIREWALL_REASON_MIN_AGE_NOT_MET\`.
    - Filter: \`spec.package_license\`
    Supported values: String
    Description: License detected on the package. Use when the reason is set to \`PACKAGE_FIREWALL_REASON_RESTRICTED_LICENSE\`.

    `}
</YamlTable>

<Note>
  Artifactory uses the API key created with `SYSTEM_ROLE_PACKAGE_FIREWALL` to route traffic through the Package Firewall. It does not grant access to the Package Firewall Log API.

  To query logs, create an API key with at least the **Read-only** role. For more information about roles and permissions, see [Authorization roles](/platform-administration/rbac/authorization-roles).
</Note>

## Troubleshooting and FAQ

<AccordionGroup>
  <Accordion title="Does the Package Firewall block installation of all versions of a malicious package?">
    No. The Package Firewall blocks only the specific package versions that Endor Labs flags as malware in the malware database.
  </Accordion>

  <Accordion title="How does the Package Firewall handle transitive dependencies?">
    The Package Firewall checks each package individually when the client requests it during installation. If any dependency, direct or transitive, contains malware, the firewall blocks that specific package installation and stops the overall installation process.
  </Accordion>

  <Accordion title="Does the Package Firewall slow down package installations?">
    No. The impact on installation time is minimal.
  </Accordion>

  <Accordion title="What if Artifactory has already cached a package that is declared malicious later?">
    If Endor Labs flags a package as malware after Artifactory cached it, Artifactory continues to serve it until the cache expires. Use a short cache duration to reduce that window.
  </Accordion>

  <Accordion title="How do I troubleshoot connection issues?">
    * Verify that the Package Firewall URL in Artifactory is correct.
    * Ensure network connectivity from Artifactory to the Package Firewall.
    * Ensure the firewall rules allow outbound connections from Artifactory.
  </Accordion>

  <Accordion title="How do I troubleshoot authentication issues?">
    * Verify the API key and secret are correct and that the key has the **SYSTEM\_ROLE\_PACKAGE\_FIREWALL** role.
    * Ensure that the credentials are in the format Artifactory expects.
    * Check the Artifactory logs for authentication errors.
  </Accordion>

  <Accordion title="How do I troubleshoot cache issues?">
    * Verify that Artifactory has sufficient storage for the cache.
    * Set cache expiration to short durations so that more requests hit the Package Firewall.
    * Check the cache hit and miss rates. Clear the cache if you need to test with a fresh request.
  </Accordion>

  <Accordion title="What happens with package version ranges like `npm install express@^4.0.0`?">
    The package manager resolves version ranges as usual. The Package Firewall checks the resolved version when the client requests it for download. If Endor Labs flags that version as malware, the firewall blocks the installation.
  </Accordion>
</AccordionGroup>
