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

# Coding Agent Governance policies

> Block dangerous commands, restrict file access, and gate MCP server use with allow and deny patterns for AI coding agents.

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

Coding Agent Governance policies tell hooks how to react to risky agent activity in real time. Each policy combines a template, a set of patterns, and an action. When a matching event arrives, the hook applies the action before the agent runs the operation.

Endor Labs ships nearly 50 system policies. Almost all ship enabled by default, so enforcement starts as soon as you deploy hooks. They block destructive commands, secret and credential access, risky git and publish operations, known-malicious MCP servers, and attempts to bypass the Package Firewall. They also ask before riskier-but-routine actions, such as installing packages or force-pushing a branch. See [Out-of-the-box policies](#out-of-the-box-policies) for the complete list.

You can change any system policy for your namespace. You can override it with a policy of the same name and a different action, turn it off in the policy list, or author your own from the same templates. See [Policy propagation across namespaces](#policy-propagation-across-namespaces).

The following templates are available:

* [**File Access**](#file-access): control reads, writes, edits, and deletes in the workspace.
* [**Command Execution**](#command-execution): control the shell commands an agent runs.
* [**MCP Server Access**](#mcp-server-access): control which MCP servers and tools an agent calls.
* [**MCP Server Posture**](#mcp-server-posture): match MCP servers by name or by Endor Labs score.
* [**Skill Access**](#skill-access): control which skills run, by name or by Endor Labs score.
* [**Session Posture**](#session-posture): match sessions by user, sandbox, and models.
* [**Malware**](#malware): check the packages an agent installs against the Endor Labs malware feed.
* [**Secrets**](#secrets): scan content the agent writes or reads for leaked secrets.

Hooks evaluate events against the policy snapshot they read from the namespace.

## Manage Coding Agent Governance policies

To open Coding Agent Governance policies:

1. Select **Policies & Rules** from the left sidebar.
2. Select **Agent Governance**.

### View Coding Agent Governance policies

On the **Agent Governance** listing, you can view policies grouped by what they govern:

* **All Policies** for every policy in scope.
* **Coding Agents** for command execution policies.
* **File Access** for file-operation policies.
* **MCP Servers** for MCP server and tool access policies.
* **MCP Server Posture** for policies that match MCP servers by Endor Labs score.
* **Sessions** for session posture policies.
* **Skills** for skill policies.

### Create a Coding Agent Governance policy

1. Click **Create Agent Governance Policy**.
2. Under **Define a Policy**, choose a template under **Policy Template**:
   * **File Access** under **Coding Agents**: Control reads, writes, deletes, and edits in the workspace.
   * **Command Execution** under **Coding Agents**: Control the shell commands the agent runs.
   * **MCP Server Access** under **MCP Servers**: Control which MCP servers and tools the agent can call.
   * **MCP Server Posture** under **MCP Servers**: Match MCP servers by name or by Endor Labs score.
   * **Skill Access** under **Skills**: Control which skills can run, by name or by Endor Labs score.
   * **Session Posture** under **Sessions**: Match sessions by user identity, sandbox, and models.
3. Under **List Type**, choose **Allowlist** to permit only matching items, or **Blocklist** to block matching items.
4. Fill in the pattern fields the template exposes. See [Templates and patterns](#templates-and-patterns) for field names and syntax.
5. Under **Choose an Action**, select the action your hook should take when an event matches:
   * **Block** stops the action and returns a deny response to the agent.
   * **Alert** lets the action proceed and records the event under **Policy Violations**.
   * **Ask Permission** pauses the agent until the developer approves or denies.
6. Optional: Under **Inventory Classification**, choose **Allow**, **Block**, **Suspicious**, or **Malicious** to tag the MCP servers or skills the policy matches. **None**, the default, applies no tag. The selector appears for the **MCP Server Posture** and **Skill Access** templates. See [Inventory classification](#inventory-classification).
7. Optional: Fill in **User Message**. The developer sees it when the policy fires, and the agent receives it inside a standard deny response that names the policy category and tells the agent not to work around the block. Endor Labs stores the **Agent Message** field on the policy but does not currently deliver it to the agent.
8. Under **Name Your Policy**, enter a **Name** that describes the policy intent.
9. Click **Create Agent Governance Policy**.

The policy is now active. Local hooks pick it up at the next session start.

<Note>
  The form groups templates by what the agent is doing, while the **Agent Governance** policy listing groups them by what they govern. **File Access** is grouped under **Coding Agents** in the form, but its policies appear under **File Access** in the listing.
</Note>

## Coding Agent Governance templates and patterns

Each template targets one activity type and exposes the fields that activity uses.

All pattern fields use regular expressions, not globs. Anchor with `^` and `$` to match the full string, and escape literal characters such as `.` and `/`.

### File Access

Use this template to govern reads, writes, deletes, and edits in the workspace.

* **File Operation**: Choose **Any**, **Read**, **Write**, **Delete**, or **Edit**.
* **File Name Patterns**: Regex patterns matched against file paths. Add one pattern per row with **+ Add file pattern**.
* **Example blocklist patterns**: `.*\.env$`, `.*\.pem$`, `.*/secrets/.*`.

### Command Execution

Use this template to govern shell commands the agent runs.

* **Custom Command Patterns**: Regex patterns matched against shell commands. Add one pattern per row with **+ Add command**.
* **Example blocklist patterns**: `^rm\s+-rf\s+/`, `^curl\s+.*\|\s*(sh|bash)\b`.

### MCP Server Access

Use this template to govern which MCP servers and tools your agents can call.

* **Server Name Patterns**: Regex patterns matched against MCP server names. Add one pattern per row with **+ Add server pattern**.
* **Tool Name Patterns**: Regex patterns matched against MCP tool names. Add one pattern per row with **+ Add tool pattern**.
* **Example blocklist patterns**: `.*-prod$` for any production server, or `.*delete.*` for tool names that include `delete`.

### MCP Server Posture

Use this template to match MCP servers by name, by Endor Labs score, or both. It scores server-level posture rather than individual tool calls, so reach for it when you want to gate on how risky a server is.

* **Server Name Patterns**: Regex patterns matched against MCP server names.
* **Overall score at or below**: Match when the server's Endor Score (`1`-`10`, higher is safer) is at or below this value. Leave blank to ignore the overall score.
* **Per-dimension thresholds**: Optionally match on one dimension instead of the overall score: **Supply Chain & Provenance**, **Authentication & Authorization**, **Operational Hygiene**, or **Input Validation & Injection**.

When you set both a name pattern and a score threshold, a server must match the name and a threshold. See [Endor Score](/agent-governance/endor-score) to learn how Endor Labs computes MCP server scores.

### Skill Access

Use this template to control which agent skills can run, by name, by Endor Labs skill score, or both.

* **Skill Name Patterns**: Regex patterns matched against skill names.
* **Overall score at or below**: Match when the skill's Endor Score (`1`-`10`, higher is safer) is at or below this value. Leave blank to ignore the overall score.
* **Per-dimension thresholds**: Optionally match on one dimension: **Instruction Integrity**, **Data Protection**, **Permission Boundaries**, or **External Dependencies**.

When you set both a name pattern and a score threshold, a skill must match the name and a threshold. See [Endor Score](/agent-governance/endor-score) to learn how Endor Labs computes skill scores.

### Session Posture

Use this template to match agent sessions by posture: who runs the session, whether it is sandboxed, and which models it uses. Each group is optional, so leave a group empty to ignore it.

* **User name patterns**: Regex patterns matched against the session user, such as `svc-.*` or `.*@example\.com`.
* **Sandbox enabled**: Match sessions where the sandbox is enabled, disabled, or either.
* **Default model patterns**: Regex patterns matched against the session's default model, such as `gpt-.*`.
* **Available model patterns**: Regex patterns matched against the models offered to the session, such as `claude-.*`.

#### Match on more session configuration through the REST API

The policy form exposes the four criteria above. Through the REST API, a session policy can match on more of the session's configuration, read from the agent's config at any level (repository, user, or enterprise). Every group you set must match, and an empty group is ignored. Set these fields on the session activity.

* `composer_mode_matchers`: Regex matched against the composer mode, such as `agent`, `ask`, or `edit`.
* `source_matchers`: Regex matched against how the session started, such as `startup`, `resume`, or `clear`.
* `is_background_agent`: Match sessions by whether they run as a background agent.
* `sandbox.fail_if_unavailable`: Match sessions by whether they require the sandbox to be available.
* `sandbox.allowed_host_matchers`: Regex matched against the sandbox's allowed network hosts, to catch overly permissive rules such as `.*`.
* `permissions.default_mode_matchers`: Regex matched against the permission default mode, such as `bypassPermissions`.
* `permissions.bypass_disabled`: Match sessions by whether permission bypass is disabled.
* `permissions.allowed_command_matchers` and `permissions.denied_command_matchers`: Regex matched against the session's allowed and denied command lists.
* `hook.event_name_matchers`: Regex matched against configured hook event names, such as `PostToolUse`.
* `hook.command_matchers`: Regex matched against configured hook command strings, such as `curl .* | bash`.

Manage a policy that uses these fields through the API end to end. Saving a policy from the form rebuilds its activity from the fields the form exposes, which silently drops any REST-only criteria.

#### Match on developer identity

When you enroll the Anthropic Compliance API integration, session policies can also match the developer's organization identity. Endor Labs syncs your Claude Enterprise directory on a schedule and resolves each Claude Code session to a directory entry by email. The hook reads the developer's email from the local git configuration (falling back to the `GIT_AUTHOR_EMAIL` and `EMAIL` environment variables), and Endor Labs matches it against the corporate email in the synced directory, so resolution depends on developers committing under their corporate email. Set these criteria through the REST API. The policy form does not expose them yet.

* `role_matchers`: Regex patterns matched against the developer's organization roles. Any match counts.
* `group_matchers`: Regex patterns matched against the developer's organization groups. Any match counts.
* `managed_status_match`: Match sessions from a managed account (in your directory) or an unmanaged one (a Claude account outside your directory, a shadow-AI signal).

Every identity criterion you set must match. When hooks can't resolve a session's identity, they skip policies that use identity criteria, so they never block an unresolved session by mistake. Identity matching applies to Claude Code sessions and to session-level events only.

To enroll, create an integration with your Anthropic Compliance API access key through the REST API. The sync stores directory entries (email, display name, roles, and groups) in your Endor Labs namespace, so review that data flow with your privacy team.

### Malware

Use this template to check the packages an agent installs against the Endor Labs malware feed. The policy carries no patterns to author. Matching the activity means running the check, and the block or allow verdict comes from the feed.

* **Ecosystems**: Optional list that narrows the check to specific ecosystems, such as `npm` or `pypi`. An empty list checks every ecosystem.

Create and edit malware policies through the REST API. See [Secure coding checks](/agent-governance/secure-coding#malware-check) for what the check inspects, the evidence a violation carries, and a full creation example.

### Secrets

Use this template to scan content the agent writes or reads for secrets, using the Endor Labs secret detection rules. The policy carries no patterns to author. Matching the activity means running the scan, and the verdict comes from the detection rules.

* **Include file patterns**: Optional regex list that limits scanning to matching paths. An empty list scans every file the event covers.
* **Exclude file patterns**: Optional regex list that skips paths such as test fixtures or vendored directories. Exclusions apply after inclusions.

Create and edit secrets policies through the REST API. See [Secure coding checks](/agent-governance/secure-coding#secrets-check) for what the scan detects and what a violation carries.

## Allowlist or blocklist

Choose **Allowlist** when you want to permit a small, known set of items and block everything else. Use it for sensitive areas, such as production credentials or a curated set of approved MCP servers.

Choose **Blocklist** when you want to permit most activity and block known-dangerous items. Use it for destructive commands, leaked-secret paths, and unapproved MCP servers.

## Actions

Each policy applies one of three actions on a match.

<YamlTable>
  {`
    - Action: **Block**
    Developer machine: The agent stops the action. The developer sees the **User Message**.
    Endor Labs: A row appears under **Policy Violations** with the action recorded as **Blocked**.
    - Action: **Ask Permission**
    Developer machine: The agent pauses until the developer confirms or denies.
    Endor Labs: A row appears under **Policy Violations** with the action recorded as **Ask**.
    - Action: **Alert**
    Developer machine: The action proceeds. The developer sees nothing.
    Endor Labs: A row appears under **Policy Violations** with the action recorded as **Alert**.
    `}
</YamlTable>

## Inventory classification

A policy can tag the MCP servers and skills it matches in your inventory, in addition to taking an action. Choose a value under **Inventory Classification** when you create a policy from the **MCP Server Posture** or **Skill Access** template. The tag is independent of the action, so a policy can block and tag, tag only, or act only.

The tag lands on the inventory record on one of two independent axes:

* Review status: **Allow** marks the item **Approved**. **Block** marks it **Blocked** (skills show **Unapproved**).
* Threat level: **Suspicious** or **Malicious**.

When several policies match the same item, the strongest value per axis wins: **Block** beats **Allow**, and **Malicious** beats **Suspicious**. The inventory record keeps the name of the policy that set each tag as evidence. Classification follows your current policy set, so turning a policy off lets the next event reclassify the item. Items no classification policy has matched show **Unreviewed**.

These tags feed the **Approved & Blocked** and **Suspicious & Malicious** summaries on the inventory pages. See [Review your agent inventory](/agent-governance/inventory) to read them.

## Scope a policy by repository or user

Through the REST API, a policy can carry a scope that limits which events it applies to. A scope holds two regex lists:

* `scope.repository_matchers`: The event's repository must match at least one pattern.
* `scope.user_matchers`: The event's user must match at least one pattern.

Within a list, any match counts. When you set both lists, both must match. A policy without a scope applies to every event, and an event that misses the scope skips the policy for both allowlists and blocklists.

The policy form does not expose scope fields yet, but editing a scoped policy in the form preserves its scope. Repository scoping does not yet take effect for Claude Code sessions, which report no repository value. User scoping works across agents today.

Scope differs from the **Session Posture** template: a session policy matches session events only, while a scope restricts a policy of any template.

## Out-of-the-box policies

Every namespace inherits these system policies. Most ship enabled by default and appear in the **Agent Governance** policy list next to the policies you create, under the exact names shown below. An override matches on the policy name, so use these names verbatim when you override one. To change a policy for a namespace, override it instead of deleting it. See [Policy propagation across namespaces](#policy-propagation-across-namespaces) to scope an override.

### MCP server policies

These policies govern MCP server and tool calls.

<YamlTable>
  {`
    - Policy: MCP tool call: match dangerous tool patterns
    What it matches: Database or SQL MCP tools whose input looks like \`delete\` or \`drop\`
    Action: Ask
    - Policy: MCP: blocklist — known-malicious MCP servers
    What it matches: MCP servers on the known-malicious blocklist (\`mcp-runcmd-server\`, \`mcp-runcommand-server\`, \`mcp-runcommand-server2\`), reported by JFrog as reverse-shell payloads
    Action: Block
    `}
</YamlTable>

### Command execution policies

These policies govern the shell commands an agent runs. Blocked commands stop outright. The rest ask the developer to confirm.

<YamlTable>
  {`
    - Policy: CmdLine: block destructive filesystem commands
    What it matches: \`rm -r\`, \`dd\`, \`mkfs\`, \`shred\`, and \`find\` with \`-delete\` or \`-exec rm\` against system or home directories
    Action: Block
    - Policy: CmdLine: block privilege escalation commands
    What it matches: \`sudo\`, \`su\`, \`doas\`, \`chown\`, \`chmod 777\`
    Action: Block
    - Policy: CmdLine: block inline code execution
    What it matches: \`python -c\`, \`node -e\`, \`bash -c\`, \`sh -c\`, or \`eval\` when the snippet shells out, reaches the network, or evaluates code
    Action: Block
    - Policy: CmdLine: block download-and-execute pipelines
    What it matches: A \`curl\` or \`wget\` download piped into a shell
    Action: Block
    - Policy: CmdLine: block dangerous git operations
    What it matches: \`git commit --no-verify\`, \`git reset --hard\`
    Action: Block
    - Policy: CmdLine: block direct push to main/master
    What it matches: \`git push\` to \`main\` or \`master\`
    Action: Block
    - Policy: CmdLine: block package and artifact publish commands
    What it matches: \`npm publish\`, \`yarn publish\`, \`pnpm publish\`, \`cargo publish\`, \`gem push\`, \`twine upload\`, \`docker push\`, \`docker buildx build --push\`, \`helm push\`
    Action: Block
    - Policy: CmdLine: block network exfiltration tools
    What it matches: \`nc\`, \`ncat\`, \`ftp\`, and \`scp\` or \`rsync\` with a remote destination
    Action: Block
    - Policy: CmdLine: block shell reads of secret files
    What it matches: \`cat\`, \`xxd\`, and similar commands that read \`.env\`, key material, or other credential files
    Action: Block
    - Policy: CmdLine: block shell writes to shell profile files
    What it matches: Shell-side writes to startup files, including redirects (\`>>\`), \`tee\`, \`sed -i\`, \`cp\` or \`mv\` onto a profile, and interpreter one-liners that write one. Reads stay clear
    Action: Block
    - Policy: CmdLine: ask before force push
    What it matches: \`git push --force\` or \`--force-with-lease\` (force pushes that name \`main\` or \`master\` are blocked instead)
    Action: Ask
    - Policy: CmdLine: ask before network download or remote access
    What it matches: \`wget\`, \`ssh\`, and \`curl\` when it uploads or sends a write request (\`-d\`, \`-F\`, \`-T\`, or \`-X POST\`, \`PUT\`, \`PATCH\`, \`DELETE\`)
    Action: Ask
    - Policy: CmdLine: ask before environment variable export
    What it matches: \`export\` of a variable whose name looks credential-bearing (contains \`TOKEN\`, \`KEY\`, \`SECRET\`, \`PASSWORD\`, \`PASSWD\`, \`CREDENTIAL\`, or \`AUTH\`)
    Action: Ask
    - Policy: CmdLine: ask before package installation or container commands
    What it matches: \`npm install\`, \`pip install\`, \`cargo install\`, \`gem install\`, \`docker run\`, \`docker pull\`
    Action: Ask
    - Policy: CmdLine: ask before recursive grep
    What it matches: Recursive \`grep -r\`, \`rg\`, or \`rgrep\` that can sweep secret values into the transcript
    Action: Ask
    `}
</YamlTable>

### File access policies

These policies block access to sensitive paths. They match reads, writes, edits, and deletes, unless noted.

<YamlTable>
  {`
    - Policy: FileAccess: block access to env files and secrets
    Paths it blocks: \`.env\`, \`.dev.vars\`, \`secrets/\`, \`config/database.yml\`, \`.netrc\`, and any file whose name ends in \`secret\`, \`secrets\`, \`credential\`, or \`credentials\`. Reads only
    - Policy: FileAccess: block access to TLS/PKI key material
    Paths it blocks: \`.key\`, \`.pem\`, \`.p12\`, \`.pfx\`, \`.crt\`. Reads only
    - Policy: FileAccess: block access to SSH keys and config
    Paths it blocks: \`~/.ssh/\`
    - Policy: FileAccess: block access to cloud provider credentials
    Paths it blocks: \`~/.aws/\`, \`~/.azure/\`, \`~/.config/gcloud/\`
    - Policy: FileAccess: block access to git credential files
    Paths it blocks: \`.gitconfig\`, \`.git-credentials\`, \`.git/config\`
    - Policy: FileAccess: block access to container and Kubernetes credentials
    Paths it blocks: \`~/.docker/config.json\`, \`~/.kube/\`, \`~/.config/helm/\`
    - Policy: FileAccess: block access to package manager token files
    Paths it blocks: \`~/.cargo/credentials\`, \`~/.cargo/credentials.toml\`, \`.pypirc\`, \`~/.gem/credentials\`
    - Policy: FileAccess: block access to vault and password manager tokens
    Paths it blocks: \`.vault-token\`, \`~/.config/op/\` (1Password), \`~/.config/bw/\` (Bitwarden)
    - Policy: FileAccess: block access to shell history files
    Paths it blocks: \`.bash_history\`, \`.zsh_history\`, fish history
    - Policy: FileAccess: block access to CI/CD and platform deployment tokens
    Paths it blocks: \`~/.config/gh/\`, \`~/.fly/\`, \`~/.config/heroku/\`, \`~/.config/vercel/\`
    - Policy: FileAccess: block access to Terraform credentials
    Paths it blocks: \`.terraformrc\`, \`~/.terraform.d/\`
    - Policy: FileAccess: block access to database credential files
    Paths it blocks: \`.pgpass\`, \`.my.cnf\`, \`~/.mongosh/\`
    - Policy: FileAccess: block access to cryptocurrency wallet data
    Paths it blocks: Application data for MetaMask, Electrum, Exodus, Phantom, and Solflare
    - Policy: FileAccess: block access to GPG keys and system keychains
    Paths it blocks: \`~/.gnupg/\`, macOS \`~/Library/Keychains/\`
    - Policy: FileAccess: block writes to shell profile files
    Paths it blocks: \`.zshrc\`, \`.zshenv\`, \`.zprofile\`, \`.zlogin\`, \`.bashrc\`, \`.bash_profile\`, \`.bash_login\`, \`.profile\`, to prevent persistence
    - Policy: FileAccess: block writes to GitHub Actions workflows
    Paths it blocks: Writes under \`.github/workflows/\`. Companion policies block edits and deletes (\`FileAccess: block edits to GitHub Actions workflows\`, \`FileAccess: block deletes of GitHub Actions workflows\`). Reads are allowed
    - Policy: FileAccess: block self-modification of Claude configuration (write)
    Paths it blocks: Writes to \`.claude/settings.json\`, \`.claude/settings.local.json\`, \`.claude/CLAUDE.md\`, \`.claude/hooks/\`. Companion \`(edit)\` and \`(delete)\` policies cover the other operations. Reads are allowed. Stops the agent loosening its own guardrails
    `}
</YamlTable>

### Package Firewall bypass protection

These policies stop an agent from bypassing the Endor Labs Package Firewall by changing where a package manager fetches packages. They ship enabled and block on a match.

The table groups the protection by theme. The **Agent Governance** policy list shows the 10 underlying system policies under their own names, each prefixed with `Package Firewall`.

<YamlTable>
  {`
    - Policy: Registry override on install
    What it blocks: A package install that overrides the registry or index (\`npm install --registry\`, \`pip install --index-url\`), or that installs from a URL, \`git+\` source, or local archive (\`.whl\`, \`.tgz\`, \`.tar.gz\`). Covers npm, pnpm, yarn, npx, pip, uv, bun, gem, mvn, and cargo
    - Policy: Direct download from registries or mirrors
    What it blocks: Fetching package content straight from public registries or CDNs (npmjs, PyPI, jsDelivr, unpkg, esm.sh, and similar) with \`curl\`, \`wget\`, or an inline interpreter
    - Policy: Package manager config change
    What it blocks: Config commands or environment overrides that repoint a package manager (\`npm config set registry\`, \`pip config set index-url\`, \`go env -w GOPROXY\`, \`PIP_INDEX_URL\`), and any write, edit, or delete of a config file such as \`.npmrc\`, \`pip.conf\`, \`.yarnrc.yml\`, \`bunfig.toml\`, \`uv.toml\`, \`.netrc\`, \`.cargo/config.toml\`, or \`.gemrc\`
    - Policy: Package manager cache tampering
    What it blocks: Populating or writing a package manager cache to stage an offline install (\`npm cache add\`, \`pip install --no-index\`, or writing into the npm or pip cache directories)
    `}
</YamlTable>

### Malware protection

This policy runs the [malware check](/agent-governance/secure-coding#malware-check) instead of matching authored patterns.

<YamlTable>
  {`
    - Policy: Malware: block installs of known-malicious packages
    What it matches: A package install whose package the Endor Labs malware feed confirms as malicious, in any ecosystem
    Action: Block
    `}
</YamlTable>

## View, edit, or delete a policy

1. Select **Policies & Rules** from the left sidebar, then select **Agent Governance**.
2. Select the policy name to open its full configuration.
3. To change the policy, edit any field and click **Update Agent Governance Policy**.
4. To remove the policy, confirm in the **Delete this Policy?** prompt. Hooks pick up the deletion at the next session start.

Every policy has a shareable URL that includes the namespace and policy name, so you can send a teammate a direct link to a specific policy.

## Local policy cache

When the agent starts a session, [endorctl ai-audit](/developers-api/cli/commands/ai-audit) pulls a snapshot of your policies and writes it to `~/.endorctl/aigovernance/endor-policies.jsonl`. The hook evaluates every event in that session against the cached snapshot, so pattern-based enforcement happens without a network round-trip. Malware policies are the exception: a matching install triggers a live malware feed lookup, and the check fails open if the feed is unreachable.

The cache file uses one JSON object per line, each representing a single policy. You do not need to edit it. If the policy download fails at session start, the hook keeps the last snapshot it fetched, so your overrides stay in effect.

<Note>
  Policy edits take effect at the next session start. New and edited policies take effect on a developer's machine the next time the agent starts a session and refreshes the local policy cache. Tell developers to restart the agent (or open a new session) to pick up policy edits.
</Note>

## Policy propagation across namespaces

Policies you create in a namespace automatically propagate to its sub-namespaces. The policy form always saves with propagation on, so any policy you save in `acme` shows up for `acme.team-a`, `acme.team-a.proj-1`, and every other descendant. Through the REST API, set the `propagate` field to `false` to keep a policy out of sub-namespaces.

When the **Agent Governance** policy list loads for a namespace, it returns:

* Policies authored in that exact namespace.
* Policies authored in any ancestor namespace.
* In-built system policies that Endor Labs ships.

If two policies share the same name across these layers, the closest match wins. For namespace `acme.team-a.proj-1`, a policy named `block-rm-rf` saved in `acme.team-a` overrides the same name in `acme` or the system catalog. A copy saved in `acme.team-a.proj-1` itself overrides all three.

Use this to scope a change narrowly:

* To turn a system or inherited policy off in one namespace, toggle it off from the **Agent Governance** policy list. This creates a local override in that namespace and leaves the policy unchanged in ancestors and siblings.
* To change a system or inherited policy, override it: save a policy with the same name in the namespace where you want the change. Use a team's namespace to scope it to that team, or the tenant root to apply it tenant-wide.
* Toggle a system policy off rather than trying to delete it.

## Next steps

Continue with the following pages:

* See [Deploy hooks for Cursor](/agent-governance/cursor), [Claude Code](/agent-governance/claude-code), [Codex](/agent-governance/codex), or [GitHub Copilot](/agent-governance/copilot) to put your policies in front of agent activity.
* See [Triage policy violations](/agent-governance/policy-violations) to set your daily review workflow.
