Skip to content

[Security]: get_read_block_error does not block project-local .env files, so read_file/ACP can read workspace secrets #20734

@Ramadas108

Description

@Ramadas108

Summary

agent/file_safety.py:get_read_block_error() on origin/main blocks Hermes internal hub cache files, but still allows reads of common project-local environment files such as:

  • .env
  • .env.local
  • .env.development
  • .env.production
  • .env.test
  • .env.staging
  • .envrc

Because both top-level read_file and the Copilot ACP fs/read_text_file path consult this helper, the missing policy leaks through both surfaces.

Current origin/main behavior

On origin/main, get_read_block_error() only blocks internal hub cache paths:

def get_read_block_error(path: str) -> Optional[str]:
    """Return an error message when a read targets internal Hermes cache files."""
    resolved = Path(path).expanduser().resolve()
    hermes_home = _hermes_home_path().resolve()
    blocked_dirs = [
        hermes_home / "skills" / ".hub" / "index-cache",
        hermes_home / "skills" / ".hub",
    ]
    ...
    return None

There is no deny rule for project-local secret-bearing env basenames.

Why this matters

Even if home-directory credential paths and HERMES_HOME credential stores are handled separately by other open work, the most common place application secrets live in real repositories is the project directory itself:

  • ./.env
  • ./services/api/.env.production
  • ./web/.env.local

Today those files are still readable through the shared read guard helper.

Because ACP reuses the same helper (agent/copilot_acp_client.py calls get_read_block_error() before fs/read_text_file), this is not just a read_file issue — it also affects ACP file reads inside the workspace.

Reproduction

On origin/main:

from agent.file_safety import get_read_block_error

print(get_read_block_error("/tmp/project/.env"))
print(get_read_block_error("/tmp/project/.env.production"))
print(get_read_block_error("/tmp/project/.env.local"))

Actual behavior

All return None, so the read is allowed.

Likewise, top-level read_file("/tmp/project/.env.production") proceeds instead of being denied.

Expected behavior

Common secret-bearing env files inside arbitrary project directories should be denied by the shared read guard helper, while obviously non-secret examples such as .env.example should remain readable.

Suggested basename deny set:

  • .env
  • .env.local
  • .env.development
  • .env.production
  • .env.test
  • .env.staging
  • .envrc

Allowed example:

  • .env.example

Why this is distinct from existing reports

This is related to, but not the same as:

Those are valuable, but they do not address the project-local .env* basename gap in the central helper on origin/main.

Suggested fix

Extend agent/file_safety.py:get_read_block_error() with a basename check for common secret-bearing project env files, using the resolved filename only (not a hardcoded directory), for example:

blocked_project_env_names = {
    ".env",
    ".env.local",
    ".env.development",
    ".env.production",
    ".env.test",
    ".env.staging",
    ".envrc",
}
if resolved.name.lower() in blocked_project_env_names:
    return "Access denied ..."

This keeps the policy centralized so both:

  • tools/file_tools.py:read_file_tool, and
  • agent/copilot_acp_client.py:fs/read_text_file

benefit automatically.

Validation we performed locally

A local patch extending the helper and re-verifying both call paths passed:

python3 -m py_compile agent/file_safety.py \
  tests/agent/test_copilot_acp_client.py \
  tests/tools/test_file_read_guards.py \
  tests/agent/test_file_safety.py

python3 -m pytest -o addopts='' \
  tests/agent/test_copilot_acp_client.py \
  tests/tools/test_file_read_guards.py \
  tests/agent/test_file_safety.py -q

Result:

  • 49 passed, 1 warning

The added coverage specifically proved:

  • internal Hermes hub cache reads are denied
  • sensitive home dirs such as ~/.ssh are denied
  • project secret files such as .env and .env.production are denied
  • ACP file reads respect the same shared guard
  • normal files and .env.example remain allowed

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High — major feature broken, no workaroundcomp/acpAgent Communication Protocol adaptercomp/toolsTool registry, model_tools, toolsetstool/fileFile tools (read, write, patch, search)type/securitySecurity vulnerability or hardening

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions