> ## 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.

# Findings

> View, filter, and manage security findings across your projects and packages.

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>;
};

A finding is a discovery of significance made following the completion of a scan. Findings result from the default out-of-the-box implementation of rule sets called [Finding policies](/platform-administration/policies/finding-policies).

## View findings

To view different types of findings associated with all projects or packages in your tenant:

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

2. Search for findings using [basic filters](#search-for-findings-using-basic-filters).

3. Use **Saved Filters** to create and [save](#saved-filters) your frequently used searches, helping you save time.

4. Toggle **Advanced** and search for findings using [advanced filters](#search-for-findings-using-advanced-filters).

5. Use **Table preferences** to select the columns you want to view and customize the appearance of the findings table.

6. Select a finding to view the following details:
   * Project metadata
   * Risk details and remediation guidance
   * Notifications associated with the finding and Jira ticket links
   * Personalized notes for each finding. You can view notes on findings if you have tenant access. Only users with **Admin** or **Code Scanner** role can add or edit notes. See [authorization roles](/platform-administration/rbac/authorization-roles) to learn about roles and permissions.

7. To apply [exceptions to findings](#dismiss-findings-using-an-exception-policy), select findings and click **Actions** > **Add Exception**.

8. To [export findings](#export-findings), select the findings, and click **Actions** > **Export Selected** or **Export All**.

<img src="https://mintcdn.com/endorlabs-b4795f4f/NT5eBW8QVW9Cos8R/images/inventory-insights/findings/findings-ui.webp?fit=max&auto=format&n=NT5eBW8QVW9Cos8R&q=85&s=06cd1348daabf0cb3df22106897e964a" alt="Findings UI" style={{width: '80%'}} width="2618" height="1376" data-path="images/inventory-insights/findings/findings-ui.webp" />

## View findings associated with a project

To view the findings associated with a project:

1. Select **Projects** from the left sidebar.
2. Select the project for which you want to view the findings.
   The Findings page includes the list of findings specific to the project.
3. Review the list of findings. Click the finding to see its details.
4. Use **Grouped By** to group findings by attributes such as dependency, location, package, CWE, tags, code owner or rule name to filter and manage them collectively.

## Finding attributes

Finding attributes are characteristics or properties associated with each discovered issue or result obtained from a scan. These attributes could include the following details and metadata.

<YamlTable>
  {`


    - Attribute: Blocker
    Description: An action policy marked this finding as blocking.

    - Attribute: Direct
    Description: Finding applies to a direct dependency.

    - Attribute: Disputed
    Description: The CVE reported in this finding has a 'disputed' status.

    - Attribute: Exception
    Description: An exception policy exempts this finding from action policies.

    - Attribute: Exploited
    Description: The CVE reported in this finding is actively exploited and appears in the Known Exploited Vulnerabilities (KEV) database.

    - Attribute: External Path Only
    Description: Finding applies to a transitive dependency reachable only through external, non-OSS, project paths.

    - Attribute: First Party
    Description: Finding applies to a dependency that belongs to the same namespace.

    - Attribute: Fix Available
    Description: A fix is available for the CVE reported in this finding.

    - Attribute: Invalid Secret
    Description: Finding applies to an invalid secret.

    - Attribute: Malware
    Description: Finding applies to a malicious package.

    - Attribute: Normal
    Description: Finding applies to a normal, non-test, dependency.

    - Attribute: Notification
    Description: Finding triggered a notification based on an action policy.

    - Attribute: Phantom
    Description: Finding applies to a phantom dependency.

    - Attribute: Segment Match
    Description: Finding applies to a dependency discovered through segment-matching.

    - Attribute: Potentially Reachable Dependency
    Description: Finding applies to a potentially reachable dependency.

    - Attribute: Potentially Reachable Function
    Description: Finding applies to a potentially reachable function.

    - Attribute: Reachable Dependency
    Description: Finding applies to a reachable dependency.

    - Attribute: Reachable Function
    Description: Finding applies to a reachable function.

    - Attribute: Same Repository
    Description: Finding applies to a dependency that belongs to the same project.

    - Attribute: Self
    Description: Finding applies only to the analyzed package version, there is no dependency involved.

    - Attribute: Test
    Description: Finding applies to a dependency that is not in production code.

    - Attribute: Transitive
    Description: Finding applies to a transitive (indirect) dependency.

    - Attribute: Under Review
    Description: Finding applies to suspicious package under review.

    - Attribute: Unfixable
    Description: There is no fix available for the CVE reported in this finding.

    - Attribute: Unreachable Dependency
    Description: Finding applies to an unreachable dependency.

    - Attribute: Unreachable Function
    Description: Finding applies to an unreachable function.

    - Attribute: Valid Secret
    Description: Finding applies to a valid secret.

    - Attribute: Warning
    Description: Finding triggered a warning based on an action policy.

    - Attribute: Withdrawn
    Description: The CVE reported in this finding has a 'withdrawn' status.


    `}
</YamlTable>

## Filter findings

Use filters to narrow findings and focus on the results most relevant to your workflow. Filters help you organize findings by attributes such as severity, category, project, status, repository context, and other finding metadata so that you can investigate, triage, and remediate findings more efficiently.

You can use the following filter types to manage findings effectively.

* [**Basic filters**](#search-for-findings-using-basic-filters): Use the preset filters to quickly narrow findings using common attributes such as severity, category, project, and status.
* [**Advanced filters**](#search-for-findings-using-advanced-filters): Use advanced filters to create complex queries and combine multiple conditions based on repository context, branches, tags, and other finding attributes.

### Search for findings using basic filters

Use the following basic filters to search for information in your findings.

* **Finding Level**: Limit results by finding severity level.
* **Dismissed**: Include or exclude dismissed findings. See [Filter dismissed findings](#filter-dismissed-findings) to learn more about filtering dismissed findings.
* **Category**: Choose from CI/CD, Malware, license risks, operational risks, RSPM, GitHub Actions, SAST, AI models, containers, secrets, security, supply chain, or vulnerability and view related findings.
* **Projects**: Narrow findings by one or more project names.
* **Custom Tags**: Narrow down the list based on custom tags.
* **Attributes**: Narrow down the list based on a range of factors such as:
  * if a patch is available to fix the findings
  * if the vulnerable function is reachable
  * if the dependency is reachable
  * if the dependency originates from a current repository or a current tenant
  * if the dependency is a test dependency
  * if the dependency's discovery type is manifest, phantom, or segment match
  * if the finding originates from itself, direct, or a transitive dependency
  * filter the findings by the **Exploited** tag from **CISA KEV**
  * filter the findings by the **Warn** or **Break the Build** options set in the [action policy](/platform-administration/policies/action-policies#create-an-action-policy-from-template)
* **EPSS Probability**: Choose the Exploit Prediction Scoring System (EPSS) score range.
* **Ecosystems**: Filter by language or ecosystem.
* **Location**: Narrow findings by where they occur (for example, path or location in the repository).
* **Confidence**: Narrow findings by detection confidence.
* **SAST Languages**: Narrow SAST findings by programming language.
* **Container Layers**: Narrow container findings by image layer.
* **Remediation**: Narrow vulnerability findings by fix status. **Endor Patch Available** filters findings where an Endor-provided patch is available to fix the vulnerability. **Recommended Upgrade Available** filters findings where a recommended version upgrade is available.
* **All Time**: Choose a time range.

### Search for findings using advanced filters

Use advanced filters to create powerful queries that drill deeper into the dataset to fetch results with a specific context.

Search using the advanced filters applies to all the branches of a repository. You can retrieve results from any branch by specifying the relevant context ID or type.

For example:

* If the default branch for the `BenchmarkJava` repository uses `master`, applying filters with the context ID for `master` shows results specific to branch.

  To scan from the default branch, run the following command :

  ```bash theme={null}
  endorctl scan --path=.
  ```

* Similarly, if there is another branch named `test-branch`, switching to that branch shows results specific to the branch.

  To scan the branch, run the following command:

  ```bash theme={null}
  git checkout test-branch
  endorctl scan --path=.
  ```

After running the scans, you can view the findings specific to the branches in the Endor Labs user interface.

1. Select **Projects** from the left sidebar.
2. Select the project for which you want to view the findings.
3. A drop-down menu at the top left of the page shows the repository's default branch. Choose a different branch to view its findings.

   In the following example, `master` is the default branch. To view findings from other branches (for example, `test-branch`), choose the desired branch from the drop-down menu.

   <img src="https://mintcdn.com/endorlabs-b4795f4f/NT5eBW8QVW9Cos8R/images/inventory-insights/findings/findings-branches.webp?fit=max&auto=format&n=NT5eBW8QVW9Cos8R&q=85&s=af9138da60f7dca5dae40f00093b5165" alt="Branch dropdown" style={{width: '80%'}} width="1576" height="300" data-path="images/inventory-insights/findings/findings-branches.webp" />

<Tip>
  Check **Projects** to see the default branch of your project. To change the default branch, use `--as-default-branch` while performing the `endorctl` scan. See [scanning strategies](/scan/sca/scanning-strategies) for information on testing and monitoring different versions of your code.
</Tip>

The **Advanced Filters** use the `GetFinding` [API call](/api-reference/findingservice/getfinding) to fetch results.

The following table lists some example attributes, you can use in your custom API calls. See also example combinations below.

<YamlTable>
  {`


    - Attribute: Severity
    API_Query: \`spec.level in ["FINDING_LEVEL_CRITICAL","FINDING_LEVEL_HIGH"]\`

    - Attribute: Category
    API_Query: \`spec.finding_categories contains ["FINDING_CATEGORY_VULNERABILITY"]\`

    - Attribute: Fixable
    API_Query: \`spec.finding_tags contains ["FINDING_TAGS_FIX_AVAILABLE"]\`

    - Attribute: Reachability
    API_Query: \`spec.finding_tags contains ["FINDING_TAGS_REACHABLE_FUNCTION"]\`

    - Attribute: Ecosystem
    API_Query: \`spec.ecosystem in ["ECOSYSTEM_MAVEN"]\`

    - Attribute: EPSS score greater than 10%
    API_Query: \`spec.finding_metadata.vulnerability.spec.epss_score.probability_score > 0.1\`

    - Attribute: EPSS score less than or equal to 100
    API_Query: \`spec.finding_metadata.vulnerability.spec.epss_score.probability_score <= 1\`

    - Attribute: Only query a given project
    API_Query: \`spec.project_uuid=="UUID of the project"\`


    `}
</YamlTable>

#### Examples

Show all findings of critical vulnerability and high severity that have a fix available, with a reachable function and EPSS score greater than 10%.

```bash theme={null}
spec.level in ["FINDING_LEVEL_CRITICAL","FINDING_LEVEL_HIGH"] and spec.finding_tags contains ["FINDING_TAGS_FIX_AVAILABLE"] and spec.finding_tags contains ["FINDING_TAGS_REACHABLE_FUNCTION"] and spec.finding_metadata.vulnerability.spec.epss_score.probability_score > 0.1
```

Show vulnerabilities for a specific project.

```bash theme={null}
spec.finding_categories contains ["FINDING_CATEGORY_VULNERABILITY"] and spec.project_uuid == "660e2bc48c7d4e60a5fc692f"
```

Show vulnerabilities for a specific language in a specific project.

```bash theme={null}
spec.finding_categories contains ["FINDING_CATEGORY_VULNERABILITY"] and spec.ecosystem in ["ECOSYSTEM_PYPI"] and spec.project_uuid == "660e2bc48c7d4e60a5fc692f"
```

### Saved filters

Saved filters are customizable filter settings that users can create and reuse across projects in Endor Labs. They improve efficiency by eliminating the need to manually recreate filters. You can save the advanced search filters that you created to fetch curated search results. Saved queries are visible in the drop-down list.

To create a saved filter:

1. Select **Findings** from the left sidebar.
2. Toggle **Advanced** in the top right corner.
3. Type the query in **CUSTOMIZE FILTER**.
4. Enter a title in the field with the filter icon.
5. Click **Save New Filter**.

<img src="https://mintcdn.com/endorlabs-b4795f4f/NT5eBW8QVW9Cos8R/images/inventory-insights/findings/saved-filter.webp?fit=max&auto=format&n=NT5eBW8QVW9Cos8R&q=85&s=9ab31588e9f0a7e4fe49dd0733d24a19" alt="Create saved filter" style={{width: '80%'}} width="2262" height="1038" data-path="images/inventory-insights/findings/saved-filter.webp" />

#### Manage saved filters

To delete a saved filter:

1. Select **Settings** from the left sidebar.
2. Select **SAVED FILTERS**.
3. Click the vertical three dots on the right side of the filter you want to delete and click **Delete**.

To edit a saved filter:

1. Select **Settings** from the left sidebar.
2. Select **SAVED FILTERS**.
3. Click the vertical three dots on the right side of the filter you want to edit and click **Edit**
4. You can update the name, query, and tags.
5. Click **Update** to save the updated changes.

<img src="https://mintcdn.com/endorlabs-b4795f4f/NT5eBW8QVW9Cos8R/images/inventory-insights/findings/update-saved-filter.webp?fit=max&auto=format&n=NT5eBW8QVW9Cos8R&q=85&s=9d9b9e3e57f7f02b5825b5a01fbb7dcd" alt="Update saved filter" style={{width: '60%'}} width="1418" height="896" data-path="images/inventory-insights/findings/update-saved-filter.webp" />

## View Remediations

If you enable [upgrade impact analysis](/risk-remediation/upgrade-impact-analysis), you can view the remediation recommendation when you select a finding, if available.

Click **View Details** to view the details of the [remediation recommendation](/risk-remediation/upgrade-impact-analysis#view-remediation-recommendations).

Click **View Full Recommendation** to view all available upgrade paths.
