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

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback:

POST https://docs.endorlabs.com/feedback

```json
{
  "path": "/best-practices/project-filters/index",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.

</AgentInstructions>

# Best practices: Working with project filters

> Learn how to implement and use project filters to search, prioritize, and manage projects across your organization.

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 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>;
    return processText(text);
  };
  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}>{processBadges(row[col])}</td>)}
          </tr>)}
      </tbody>
    </table>;
};

Filters enable targeted queries based on attributes such as severity, package ecosystems, dependency resolution, and platform source.

This guide explains how filters work, how to apply and combine them effectively, and provides practical examples to support triage, audit, and reporting workflows across large codebases.

## Access project filters

Perform the following steps to apply filters to the project list.

1. Select **Projects** from the left sidebar.
2. Filter your projects using the list of available filters in the filter bar.
3. Toggle the **Advanced** option in the filter bar to apply API-style filters.

You can combine multiple filters to create more specific searches and narrow down the project list based on multiple criteria.

## How filters work

Each filter consists of three parts.

* **Field**: The attribute you want to filter (for example, `Package Count` and `Platform Source`).
* **Operator**: The comparison logic (for example, `equals`, `greater than`, and `in`).
* **Value**: The target value to evaluate (for example, `npm` and `100`).

Project filters use standard comparison operators to evaluate criteria. See [Filter operators](/developers-api/rest-api/using-the-rest-api/filters#operators) for detailed information about available operators and their usage.

When you apply multiple filters, the system combines them using logical AND operations across filters for different fields and logical OR operations across filters for the same field.

For example:

* **Filter 1**: `Package Ecosystems contains: npm`
* **Filter 2**: `Platform Source in: GitHub`
* **Filter 3**: `Package Count: greater than 1`
* **Filter 4**: `Reachability Analysis Status greater than or equal to: 90%`

<img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/best-practices/project-filters/project-filters/multiple-filters.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=fa115d0cea31a8c21b855e9f26b391c0" alt="Multiple filters" style={{width: '70%'}} width="2418" height="1120" data-path="images/best-practices/project-filters/project-filters/multiple-filters.webp" />

This combination returns only projects that use npm packages, are from GitHub, have more than one package, and have a reachability analysis status of at least 90%.

## Filter implementation techniques

You can use the following filter types to manage your projects effectively.

* [Preset filters](#preset-filters): Use predefined UI-based filters to quickly segment projects by common attributes.
* [Filter projects using API](#filter-projects-using-api): Use advanced syntax for complex queries and logical combinations.

### Preset filters

The following examples demonstrate how to apply preset filters for common project scenarios.

#### Filter projects by custom tags

Use custom tags to filter projects based on environment or predefined labels assigned during project initialization or scan configuration.

For example, to view only projects related to SAST, use the `Custom Tags contains: sast` filter.

<img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/best-practices/project-filters/project-filters/filter-custom-tags.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=e8213daf381598509fcb9d8716c77d31" alt="filter by custom tags" style={{width: '70%'}} width="2426" height="1096" data-path="images/best-practices/project-filters/project-filters/filter-custom-tags.webp" />

#### Filter projects by findings severity

Prioritize remediation efforts by filtering projects based on the severity of security findings. You can select from **Critical (C)**, **High (H)**, **Medium (M)**, or **Low (L)** severity filters to target different priority levels.

For example, to identify projects with critical findings, select the **C** filter.

<img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/best-practices/project-filters/project-filters/filter-finding-severity.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=bc9725d1414b8237656b5f26e7e8648f" alt="filter by severity" style={{width: '70%'}} width="2372" height="820" data-path="images/best-practices/project-filters/project-filters/filter-finding-severity.webp" />

#### Filter projects by package ecosystem

Use package ecosystem filters to segment projects by language or package manager. Apply targeted policies, such as stricter vulnerability thresholds for JavaScript or license compliance checks for Java.

For example, to focus on PHP projects for a security assessment, use the `Package Ecosystems contains: Packagist` filter.

<img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/best-practices/project-filters/project-filters/filter-package-ecosystem.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=8bd4d1092ab7e37b156ee20ca5ebcdbd" alt="filter by package ecosystem" style={{width: '70%'}} width="2420" height="1264" data-path="images/best-practices/project-filters/project-filters/filter-package-ecosystem.webp" />

#### Filter projects by source platform

Use platform source filters to segment projects by their source platform and correlate findings with platform-native security tools like GitHub's Dependabot alerts or GitLab's vulnerability scanning.

For example, to identify projects analyzed from GitLab, use the `Platform Source in: GitLab` filter.

<img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/best-practices/project-filters/project-filters/filter-source-platform.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=cbf3e8123e0063722247a93ede3fbf61" alt="filter by source platform" style={{width: '70%'}} width="2420" height="966" data-path="images/best-practices/project-filters/project-filters/filter-source-platform.webp" />

#### Filter projects by dependency resolution quality

Use dependency resolution status to identify projects with resolution issues that impact security analysis accuracy. You can filter by a single value or a range of percentages.

For example, to identify projects with poor dependency resolution, use the **Range** filter to find projects with `Dependency Resolution Status greater than 0% and less than 50%`.

<img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/best-practices/project-filters/project-filters/filter-dependency-resolution-status.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=df2efb9b2ef6ddb575a27379d94d4fbf" alt="filter by resolution" style={{width: '70%'}} width="2420" height="846" data-path="images/best-practices/project-filters/project-filters/filter-dependency-resolution-status.webp" />

#### Filter projects by scan timestamp

Use last scanned filters to identify projects with stale security data that require fresh scans for current security posture. You can select from predefined time ranges or use the calendar to select a specific date.

For example, to identify projects scanned within the last 24 hours, use the `Last Scanned: Last Day` filter.

<img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/best-practices/project-filters/project-filters/filter-last-scanned.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=003e7130c9d6f9c9614c35bc54bf20b1" alt="filter by last scanned" style={{width: '70%'}} width="2418" height="1216" data-path="images/best-practices/project-filters/project-filters/filter-last-scanned.webp" />

#### Filter projects by complexity

Identify projects based on their size and complexity, which may require different levels of security attention and resources.

For example, to focus on large projects with extensive dependency trees, use the `Package Count: greater than 100` filter.

<img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/best-practices/project-filters/project-filters/filter-complexity.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=c6232d078d31ed4bc09fba2fc6fc9ccc" alt="filter by complexity" style={{width: '70%'}} width="2418" height="1216" data-path="images/best-practices/project-filters/project-filters/filter-complexity.webp" />

#### Filter projects by reachability analysis status

Use reachability analysis status to identify projects based on the success rate of call graph generation and reachability analysis. You can filter by a single value or a range of percentages.

For example, to identify projects with successful reachability analysis, use the `Reachability Analysis Status greater than or equal to: 90%` filter.

<img src="https://mintcdn.com/endorlabs-b4795f4f/dHzwUrp_QbpzV9uv/images/best-practices/project-filters/project-filters/filter-reachability-analysis-status.webp?fit=max&auto=format&n=dHzwUrp_QbpzV9uv&q=85&s=c9419cef63e5273fd5d899700660716b" alt="filter by reachability status" style={{width: '70%'}} width="2418" height="858" data-path="images/best-practices/project-filters/project-filters/filter-reachability-analysis-status.webp" />

### Filter projects using API

For complex queries, use the advanced filter syntax to combine multiple attributes and apply logical operators.

The following table lists the available attributes for project filters.

<YamlTable>
  {`


    - Attribute: Project UUID
    Description: Filters projects by their unique identifier.

    - Attribute: Name
    Description: Filters projects by the display name of the project.

    - Attribute: Custom Tags
    Description: Filters projects by custom tags set during project setup or scan configuration.

    - Attribute: Last Scanned
    Description: Filters projects based on the timestamp of the last successful scan.

    - Attribute: Package Count
    Description: Filters projects by the total number of packages resolved in the project.

    - Attribute: Package Ecosystems
    Description: Filters projects based on the language-specific package ecosystems they use.

    - Attribute: Dependency Resolution Status
    Description: Filters projects by the percentage of resolved dependencies in the project.

    - Attribute: Reachability Analysis Status
    Description: Filters projects based on the success rate of reachability analysis through call graph generation.

    - Attribute: Critical Findings Count
    Description: Filters projects based on the number of critical-severity findings in the project.

    - Attribute: High Findings Count
    Description: Filters projects based on the number of high-severity findings in the project.

    - Attribute: Medium Findings Count
    Description: Filters based on the number of medium-severity findings in the project.

    - Attribute: Low Findings Count
    Description: Filters projects based on the number of low-severity findings in the project.

    - Attribute: Dismissed Findings Count
    Description: Filters projects by the number of findings that users dismissed. Useful for reviewing triage quality.

    - Attribute: Total Findings Count
    Description: Filters projects based on the total number of findings detected in a project.

    - Attribute: Platform Source
    Description: Filters projects based on source platform and helps narrow results by version control origin such as GitHub, GitLab, or Bitbucket.


    `}
</YamlTable>

#### API filter use cases

The following examples demonstrate how to combine these attributes for common security and compliance workflows.

<AccordionGroup>
  <Accordion title="Identify high-risk GitHub projects">
    Find GitHub projects with more than 5 critical findings and a reachability analysis status below 80%.

    ```bash theme={null}
    spec.platform_source == "PLATFORM_SOURCE_GITHUB" and spec.finding_counts.critical > 5 and spec.package_coverage.call_graph_success_rate < 0.8
    ```
  </Accordion>

  <Accordion title="Audit stale projects with risks">
    Identify projects not scanned in the last 7 days that still have outdated dependencies.

    ```bash theme={null}
    spec.last_scanned < now(-168h) and spec.dependency_counts.outdated > 0
    ```
  </Accordion>

  <Accordion title="Identify projects with low dependency resolution quality">
    Find projects where dependency resolution is below 90%, which may indicate incomplete security analysis.

    ```bash theme={null}
    spec.package_coverage.success_rate < 0.9
    ```
  </Accordion>

  <Accordion title="Find large projects with high vulnerability density">
    Identify projects with more than 100 total packages that also have a high number of critical vulnerabilities.

    ```bash theme={null}
    spec.package_coverage.total > 100 and spec.vulnerability_counts.total.critical > 20
    ```
  </Accordion>

  <Accordion title="Audit projects with high triaged findings">
    Find projects where more than 50 findings have been dismissed, useful for auditing triage quality.

    ```bash theme={null}
    spec.finding_counts.dismissed > 50
    ```
  </Accordion>

  <Accordion title="Triage critical vulnerabilities by ecosystem">
    Focus on npm projects with a high number of critical vulnerabilities.

    ```bash theme={null}
    spec.package_coverage.ecosystems contains ECOSYSTEM_NPM and spec.vulnerability_counts.total.critical > 10
    ```
  </Accordion>
</AccordionGroup>
