> ## 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": "/risk-remediation/index",
  "feedback": "Description of the issue"
}
```

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

</AgentInstructions>

# Risk Remediation

> Learn how Endor Labs helps address security vulnerabilities through strategic software updates and patches.

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

Software security teams face the challenge of managing thousands of dependencies across multiple projects, each with their own vulnerability landscape and upgrade requirements. Most vulnerabilities can be resolved through version upgrades which require careful analysis of compatibility, breaking changes, and dependency conflicts.

Endor Labs provides automated upgrade analysis and remediation capabilities that transform vulnerability management from reactive issue identification to proactive, action-based risk resolution. The platform analyzes entire dependency trees to identify optimal upgrade paths and generates specific remediation recommendations using the following two key components:

[**Upgrade Impact Analysis**](/risk-remediation/upgrade-impact-analysis) identifies and recommends upgrades for your dependencies. By pinpointing the distinct actions that can resolve your vulnerabilities and mitigate the risks associated with updates, your security program can make more informed risk management decisions and triage issues more effectively.

[**Endor Patches**](/risk-remediation/endor-patches) provide backported security fixes to your packages, allowing you to minimize the impact of software updates. You can update the libraries with a minimally viable security patch that reduces the risks of breaking changes, bugs, or performance issues associated with an upgrade.

[**Automated Pull Requests**](/risk-remediation/automated-pull-requests) automatically generate pull requests with dependency upgrades and security fixes directly in GitHub development workflows. This capability integrates remediation recommendations into existing CI/CD processes, enabling teams to review and merge security fixes through standard code review workflows.

<Note>
  **Maximum number of remediation PRs**

  Endor Labs creates a maximum of 20 remediation PRs per project through the GitHub App integration.
</Note>

The following diagram demonstrates an example of a vulnerability prioritization process performed by security teams:

<img src="https://mintcdn.com/endorlabs-b4795f4f/2CFeZIRm7eKUPEq0/images/risk-remediation/vuln_prioritization.webp?fit=max&auto=format&n=2CFeZIRm7eKUPEq0&q=85&s=9804c44ed0f52c435ae9efb1908f14fe" alt="Vulnerability Prioritization" width="1734" height="972" data-path="images/risk-remediation/vuln_prioritization.webp" />

## Remediation support matrix

The following table describes the level of remediation support available for different languages.

<YamlTable>
  {`


    - Language: Python
    Upgrade_recommendations: -yes-
    Identify_remediation_risk_for_conflicts: -yes-
    Identify_remediation_risk_for_breaking_changes: -yes-
    - Language: Java
    Upgrade_recommendations: -yes-
    Identify_remediation_risk_for_conflicts: -yes-
    Identify_remediation_risk_for_breaking_changes: -yes-
    - Language: .NET (C#)
    Upgrade_recommendations: -yes-
    Identify_remediation_risk_for_conflicts: -yes-
    Identify_remediation_risk_for_breaking_changes: -yes-
    - Language: Scala
    Upgrade_recommendations: -yes-
    Identify_remediation_risk_for_conflicts: -yes-
    Identify_remediation_risk_for_breaking_changes: -yes-
    - Language: Kotlin
    Upgrade_recommendations: -yes-
    Identify_remediation_risk_for_conflicts: -yes-
    Identify_remediation_risk_for_breaking_changes: -yes-
    - Language: Ruby
    Upgrade_recommendations: -yes-
    Identify_remediation_risk_for_conflicts: -yes-
    Identify_remediation_risk_for_breaking_changes: -no-
    - Language: Golang
    Upgrade_recommendations: -yes-
    Identify_remediation_risk_for_conflicts: -yes-
    Identify_remediation_risk_for_breaking_changes: -no-
    - Language: PHP
    Upgrade_recommendations: -yes-
    Identify_remediation_risk_for_conflicts: -yes-
    Identify_remediation_risk_for_breaking_changes: -no-
    - Language: Swift/Objective-C
    Upgrade_recommendations: -yes-
    Identify_remediation_risk_for_conflicts: -yes-
    Identify_remediation_risk_for_breaking_changes: -no-
    - Language: JavaScript
    Upgrade_recommendations: -yes-
    Identify_remediation_risk_for_conflicts: -yes-
    Identify_remediation_risk_for_breaking_changes: -no-
    - Language: Rust
    Upgrade_recommendations: -yes-
    Identify_remediation_risk_for_conflicts: -yes-
    Identify_remediation_risk_for_breaking_changes: -no-


    `}
</YamlTable>
