Skip to content

feat(security): block secret writes into persistent workspace memory#1632

Merged
ericksoa merged 31 commits intoNVIDIA:mainfrom
prekshivyas:security/block-secret-memory-writes
Apr 12, 2026
Merged

feat(security): block secret writes into persistent workspace memory#1632
ericksoa merged 31 commits intoNVIDIA:mainfrom
prekshivyas:security/block-secret-memory-writes

Conversation

@prekshivyas
Copy link
Copy Markdown
Contributor

@prekshivyas prekshivyas commented Apr 8, 2026

Summary

  • Registers a before_tool_call plugin hook that blocks write/edit/apply_patch/notebook_edit tool calls to persistent memory paths when secrets are detected
  • 14 high-confidence secret patterns: NVIDIA, OpenAI, Anthropic, GitHub, AWS, Slack, Discord, npm, Telegram, Google, HuggingFace tokens, private keys (PEM), authorization headers
  • Protected paths: .openclaw-data/memory/, workspace/, agents/, skills/, hooks/, MEMORY.md
  • Hook registration wrapped in try/catch so scanner failures cannot break plugin startup
  • Non-memory paths and clean content pass through unchanged

Closes #1233

Test plan

  • 21 unit tests for secret patterns (true positives, false positives, redaction, path matching)
  • 9 integration tests for hook wiring (blocks secrets in memory, allows clean content, allows non-memory paths, handles missing events)
  • TypeScript build passes
  • All 272 plugin tests pass
  • All 954 CLI tests pass
  • gitleaks + detect-private-key hooks pass (test fixtures use runtime-assembled strings)
  • Manual test: verify blocked write shows actionable error in agent session

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Automatic secret scanner that blocks write-like operations to persistent memory/workspace paths when high-confidence credentials are detected, returns an actionable block reason, and logs a security warning. Matches are redacted in reports.
  • Documentation

    • Added guidance describing the scanner, covered locations, and observable blocking behavior.
  • Tests

    • Added tests for secret detection, memory-path checks, blocking behavior, and warning logs.

…VIDIA#1233)

Registers a before_tool_call plugin hook that intercepts write/edit/
apply_patch/notebook_edit tool calls targeting persistent memory paths
(.openclaw-data/memory, workspace, agents, skills, hooks) and blocks
them when high-confidence secret patterns are detected.

14 patterns: NVIDIA, OpenAI, Anthropic, GitHub, AWS, Slack, Discord,
npm, Telegram, Google, HuggingFace tokens, private keys (PEM), and
authorization headers.

The hook returns { block: true, blockReason } with an actionable error
listing the detected patterns with redacted values. Non-memory paths
and clean content pass through unchanged. The hook registration is
wrapped in try/catch so scanner failures cannot break plugin startup.

Closes NVIDIA#1233

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a before_tool_call hook that scans write/edit-like tool calls targeting persistent memory/workspace paths for high-confidence secrets and blocks the operation with a redacted, actionable reason when matches are found.

Changes

Cohort / File(s) Summary
API Types & Hook Registration
nemoclaw/src/index.ts
Exported BeforeToolCallEvent and BeforeToolCallResult; changed OpenClawPluginApi.on handler return type to allow `BeforeToolCallResult
Secret Scanner Implementation
nemoclaw/src/security/secret-scanner.ts
New SecretMatch interface and exported scanForSecrets(content) and isMemoryPath(path) functions implementing ~14 high-confidence regex patterns, deduplication, and truncated redaction of matches.
Tests — Hook Behavior
nemoclaw/src/register.test.ts
New Vitest suite asserting the registered before_tool_call handler blocks write/edit-like operations to memory paths when secrets are present, allows clean/non-memory/non-write cases, tolerates malformed events, and logs a security warning when blocking.
Tests — Scanner
nemoclaw/src/security/secret-scanner.test.ts
Extensive tests covering each secret pattern, redaction formatting, deduplication, multi/overlapping matches, and isMemoryPath() true/false cases for anchored and non-anchored paths.
Docs — Security & Guidance
docs/security/best-practices.md, /.agents/.../references/best-practices.md
Added "Memory Secret Scanner" documentation describing the default-enabled before_tool_call hook, targeted directories/files, covered secret patterns, and blocking behavior with redacted, actionable errors.
Docs — Wording Edits
docs/about/..., docs/index.md, .agents/skills/.../SKILL.md, .agents/skills/.../references/*
Minor wording/hyphenation edits replacing “management of OpenClaw” with “OpenClaw operations” and standardizing “open-source” phrasing across several docs.

Sequence Diagram

sequenceDiagram
    participant Agent as Agent
    participant HookAPI as Hook Registration
    participant Scanner as Secret Scanner
    participant Logger as Logger
    participant Result as Block/Allow Result

    Agent->>HookAPI: Invoke write/edit-like tool (event: toolName, params)
    HookAPI->>HookAPI: Validate event & resolve target path
    alt Not write-like or not memory path
        HookAPI-->>Agent: Return undefined (allow)
    else Memory write
        HookAPI->>Scanner: scanForSecrets(content)
        Scanner->>Scanner: Apply anchored regexes, dedupe, redact
        Scanner-->>HookAPI: SecretMatch[] or []
        alt Secrets detected
            HookAPI->>Logger: Log "[SECURITY] Blocked memory write" (redacted)
            HookAPI-->>Agent: Return { block: true, blockReason }
        else No secrets
            HookAPI-->>Agent: Return undefined (allow)
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped along the memory trail,

I sniffed out keys that might derail.
I nibbled secrets, redacted tight,
Kept workspace safe throughout the night.
A rabbit guards your files with light.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes minor documentation wording updates (open-source hyphenation, management→operations phrasing) in SKILL.md and overview files that are not directly related to the security scanner implementation. Remove or separate the documentation wording changes (open-source hyphenation, management→operations in non-security docs) into a distinct PR to maintain focus on the security feature.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: implementing security controls to block secret writes into persistent workspace memory.
Linked Issues check ✅ Passed All acceptance criteria from issue #1233 are met: memory writes with secrets are rejected, high-confidence patterns are implemented narrowly, and error messages redact secret values.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
nemoclaw/src/security/secret-scanner.test.ts (1)

24-97: Missing test coverage for Discord, AWS secret key, and Authorization header patterns.

The test suite covers 12 of the 14 secret patterns but is missing tests for:

  • Discord bot token
  • AWS secret key (the lookbehind pattern)
  • Authorization header (the lookbehind pattern)

Since the lookbehind patterns have more complex matching logic, they warrant explicit test coverage to ensure they work as intended.

📝 Suggested additional test cases

Add to the FAKE object:

discord: "ABCDEFGHIJKLMNOPQRSTUVWx" + ".abc123" + ".ABCDEFGHIJKLMNOPQRSTUVWXYZa",
awsSecret: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
authHeader: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0",

Add test cases:

it("Discord bot token", () => {
  const matches = scanForSecrets(`DISCORD_TOKEN=${FAKE.discord}`);
  expect(matches).toHaveLength(1);
  expect(matches[0].pattern).toBe("Discord bot token");
});

it("AWS secret key", () => {
  const matches = scanForSecrets(`aws_secret_access_key = ${FAKE.awsSecret}`);
  expect(matches).toHaveLength(1);
  expect(matches[0].pattern).toBe("AWS secret key");
});

it("Authorization header", () => {
  const matches = scanForSecrets(`Authorization: Bearer ${FAKE.authHeader}`);
  expect(matches).toHaveLength(1);
  expect(matches[0].pattern).toBe("Authorization header");
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/security/secret-scanner.test.ts` around lines 24 - 97, The test
suite misses coverage for the Discord bot token, AWS secret key (lookbehind),
and Authorization header (lookbehind); update the FAKE test data object to
include keys discord, awsSecret, and authHeader with the suggested values and
add three tests calling scanForSecrets that assert one match and that
matches[0].pattern equals "Discord bot token", "AWS secret key", and
"Authorization header" respectively; place the new tests alongside the other
pattern tests so the lookbehind regexes exercised by scanForSecrets are covered.
nemoclaw/src/register.test.ts (1)

82-207: Good test coverage, but notebook_edit tool is not tested.

The test suite thoroughly covers write, edit, and apply_patch tools, but the notebook_edit tool (listed in WRITE_TOOL_NAMES in index.ts line 254) lacks explicit test coverage. Consider adding a test case for notebook_edit to ensure consistent behavior.

📝 Suggested test case for notebook_edit
it("blocks notebook_edit to memory path containing secrets", () => {
  const api = createMockApi();
  const handler = getHookHandler(api);
  const fakeKey = "nvapi-" + "abcdefghijklmnopqrstuvwxyz";
  const result = handler({
    toolName: "notebook_edit",
    params: {
      file_path: "/sandbox/.openclaw-data/memory/notebook.ipynb",
      content: `api key: ${fakeKey}`,
    },
  });
  expect(result).toMatchObject({ block: true });
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/register.test.ts` around lines 82 - 207, Add a test covering the
"notebook_edit" tool similar to existing tests for "write"/"edit"/"apply_patch":
call getHookHandler(api) after register(api) and invoke the handler with
toolName "notebook_edit" and params containing a memory path (e.g.
"/sandbox/.openclaw-data/memory/notebook.ipynb") and secret-containing content;
assert the handler returns { block: true } (and optionally that api.logger.warn
was called). This ensures notebook_edit (one of WRITE_TOOL_NAMES referenced in
index.ts) is handled the same as other write tools by the register/on
before_tool_call hook.
nemoclaw/src/index.ts (1)

300-301: Unnecessary String() conversion.

matches.length is already a number, and template literals handle number-to-string coercion automatically. The explicit String() is redundant.

🧹 Minor cleanup
       return {
         block: true,
         blockReason:
-          `Memory write blocked: detected ${String(matches.length)} likely secret(s):\n${summary}\n\n` +
+          `Memory write blocked: detected ${matches.length} likely secret(s):\n${summary}\n\n` +
           "Remove secrets before saving to persistent memory. " +
           "Use environment variables or credential stores instead.",
       };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/index.ts` around lines 300 - 301, The template literal for
blockReason uses an unnecessary String() conversion around matches.length;
remove the explicit String() so the expression becomes `${matches.length}` (or
just `${matches.length}` within the existing template) while keeping the summary
concatenation intact—update the code that builds blockReason (the blockReason
property that references matches and summary) to rely on template literal
coercion instead of String().
nemoclaw/src/security/secret-scanner.ts (1)

44-45: Consider tightening the Discord bot token pattern to reduce false positives.

The Discord bot token regex [A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,} could match non-secret strings that happen to have two dots separating alphanumeric segments (e.g., some base64-encoded JWTs or other structured identifiers).

Since the PR goal is high-confidence detection with low false positives, consider whether this pattern is narrow enough for the use case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/security/secret-scanner.ts` around lines 44 - 45, The current
"Discord bot token" entry in secret-scanner.ts is too permissive and can match
JWT/base64-like strings; update that entry (the object with name "Discord bot
token") to tighten detection by requiring contextual anchors (e.g., a positive
lookbehind for common labels like "discord", "bot", "DISCORD_TOKEN", "BOT_TOKEN"
or the literal "Bot " prefix) and disallow base64-only characters (exclude '='
'+' '/' in the token character classes) so the pattern only matches when the
token appears with typical surrounding text or env var names; ensure the
modified regex still matches legitimate Discord tokens but reduces false
positives by combining the contextual lookbehind and a stricter character class
for the three dot-separated segments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@nemoclaw/src/index.ts`:
- Around line 300-301: The template literal for blockReason uses an unnecessary
String() conversion around matches.length; remove the explicit String() so the
expression becomes `${matches.length}` (or just `${matches.length}` within the
existing template) while keeping the summary concatenation intact—update the
code that builds blockReason (the blockReason property that references matches
and summary) to rely on template literal coercion instead of String().

In `@nemoclaw/src/register.test.ts`:
- Around line 82-207: Add a test covering the "notebook_edit" tool similar to
existing tests for "write"/"edit"/"apply_patch": call getHookHandler(api) after
register(api) and invoke the handler with toolName "notebook_edit" and params
containing a memory path (e.g. "/sandbox/.openclaw-data/memory/notebook.ipynb")
and secret-containing content; assert the handler returns { block: true } (and
optionally that api.logger.warn was called). This ensures notebook_edit (one of
WRITE_TOOL_NAMES referenced in index.ts) is handled the same as other write
tools by the register/on before_tool_call hook.

In `@nemoclaw/src/security/secret-scanner.test.ts`:
- Around line 24-97: The test suite misses coverage for the Discord bot token,
AWS secret key (lookbehind), and Authorization header (lookbehind); update the
FAKE test data object to include keys discord, awsSecret, and authHeader with
the suggested values and add three tests calling scanForSecrets that assert one
match and that matches[0].pattern equals "Discord bot token", "AWS secret key",
and "Authorization header" respectively; place the new tests alongside the other
pattern tests so the lookbehind regexes exercised by scanForSecrets are covered.

In `@nemoclaw/src/security/secret-scanner.ts`:
- Around line 44-45: The current "Discord bot token" entry in secret-scanner.ts
is too permissive and can match JWT/base64-like strings; update that entry (the
object with name "Discord bot token") to tighten detection by requiring
contextual anchors (e.g., a positive lookbehind for common labels like
"discord", "bot", "DISCORD_TOKEN", "BOT_TOKEN" or the literal "Bot " prefix) and
disallow base64-only characters (exclude '=' '+' '/' in the token character
classes) so the pattern only matches when the token appears with typical
surrounding text or env var names; ensure the modified regex still matches
legitimate Discord tokens but reduces false positives by combining the
contextual lookbehind and a stricter character class for the three dot-separated
segments.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 117fe855-b29a-41d0-802d-c9b93aecaf3e

📥 Commits

Reviewing files that changed from the base of the PR and between b8a5245 and 5a4f540.

📒 Files selected for processing (4)
  • nemoclaw/src/index.ts
  • nemoclaw/src/register.test.ts
  • nemoclaw/src/security/secret-scanner.test.ts
  • nemoclaw/src/security/secret-scanner.ts

… missing tests

- Tighten Discord bot token pattern with contextual lookbehind to avoid
  matching JWT/base64 strings
- Add missing tests for Discord, AWS secret key, and Authorization header
- Add notebook_edit integration test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/skills/nemoclaw-configure-security/references/best-practices.md:
- Around line 1-3: Add the required SPDX header as two HTML comments immediately
before the first heading ("Security Best Practices") in this Markdown file;
insert the SPDX copyright line and the SPDX-License-Identifier line in HTML
comment form (matching the repo guideline texts) so the file begins with those
two SPDX comment lines followed by the existing "# Security Best Practices"
heading.

In @.agents/skills/nemoclaw-overview/references/overview.md:
- Line 3: Summary: use a hyphenated compound adjective for "open-source". Fix:
change the sentence that begins "NVIDIA NemoClaw is an open source reference
stack..." to use "open-source" (i.e., "NVIDIA NemoClaw is an open-source
reference stack that simplifies running..."); also update the other flagged
instance of "open source" in this file to "open-source" for consistency.
- Line 4: The intro sentence "NemoClaw provides onboarding, lifecycle
management, and management of OpenClaw within OpenShell containers." repeats
"management" and should be simplified; replace it with a single clear phrase
such as "NemoClaw provides onboarding and lifecycle management of OpenClaw
within OpenShell containers" (or "provides onboarding, lifecycle, and
operational management of OpenClaw..." if you need three distinct items) by
editing the sentence in overview.md to remove the duplicated word and ensure
parallel structure.

In @.agents/skills/nemoclaw-overview/SKILL.md:
- Around line 1-4: Prepend the two required SPDX header lines as HTML comments
at the very top of SKILL.md before the YAML front matter: add an HTML-comment
line with the SPDX-FileCopyrightText value "Copyright (c) 2026 NVIDIA
CORPORATION & AFFILIATES. All rights reserved." and a second HTML-comment line
with the SPDX-License-Identifier "Apache-2.0", ensuring they appear before the
existing --- YAML front matter block so the file complies with the SPDX header
rule for Markdown files.

In @.agents/skills/nemoclaw-reference/references/troubleshooting.md:
- Around line 1-5: Add the required SPDX header as two HTML comments immediately
before the first heading "# Troubleshooting": insert "<!--
SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All
rights reserved. -->" and "<!-- SPDX-License-Identifier: Apache-2.0 -->" at the
top of the Markdown file so the SPDX header precedes the "# Troubleshooting"
heading.

In @.agents/skills/nemoclaw-workspace/SKILL.md:
- Around line 1-4: Add the required SPDX header as two HTML-comment lines
immediately before the YAML front matter in SKILL.md: prepend the lines for
SPDX-FileCopyrightText and SPDX-License-Identifier (using HTML comments <!-- ...
-->) so they appear above the initial '---' front matter marker in
.agents/skills/nemoclaw-workspace/SKILL.md.

In `@nemoclaw/src/index.ts`:
- Around line 289-290: The code checks isMemoryPath() against the raw filePath
from event.params, so relative paths (e.g., "memory/...") bypass the
memory-prefix check; call api.resolvePath(filePath) and use its resolved result
when evaluating isMemoryPath() and subsequent logic (replace references to
filePath in this branch with the resolved path variable), ensuring you still
handle missing/empty values and keep the early return if the resolved path is
not a memory path.

In `@nemoclaw/src/security/secret-scanner.ts`:
- Around line 36-39: Update the context-anchored secret regexes to be
case-insensitive so common variants like "AWS_SECRET_ACCESS_KEY=" and
"authorization: bearer ..." are detected; specifically, add the case-insensitive
flag to the regex literal for the "AWS secret key" entry (the
/(?<=aws_secret_access_key\s*[=:]\s*)[A-Za-z0-9/+=]{40}\b/ pattern) and the
similar pattern around lines 61-64 (the authorization/bearer pattern) so they
match regardless of letter case.
- Around line 102-116: Replace the bare "/MEMORY.md" matcher and broaden the
roots to match real workspace roots instead of any basename; update
MEMORY_PATH_SEGMENTS used by isMemoryPath to remove "/MEMORY.md" and add
anchored variants like "/.openclaw/MEMORY.md" and "/.openclaw-data/MEMORY.md"
and also include the `.openclaw` workspace roots such as
"/.openclaw/workspace/", "/.openclaw/agents/", "/.openclaw/skills/",
"/.openclaw/hooks/" (in addition to the existing "/.openclaw-data/..." entries)
so isMemoryPath(filePath: string) only returns true for files under those
persistent workspace roots.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7f553992-0342-44ae-9062-edbfeb8cc2dd

📥 Commits

Reviewing files that changed from the base of the PR and between 5a4f540 and 301c44c.

📒 Files selected for processing (27)
  • .agents/skills/nemoclaw-configure-inference/SKILL.md
  • .agents/skills/nemoclaw-configure-inference/references/inference-options.md
  • .agents/skills/nemoclaw-configure-security/SKILL.md
  • .agents/skills/nemoclaw-configure-security/references/best-practices.md
  • .agents/skills/nemoclaw-configure-security/references/credential-storage.md
  • .agents/skills/nemoclaw-deploy-remote/SKILL.md
  • .agents/skills/nemoclaw-deploy-remote/references/sandbox-hardening.md
  • .agents/skills/nemoclaw-get-started/SKILL.md
  • .agents/skills/nemoclaw-manage-policy/SKILL.md
  • .agents/skills/nemoclaw-monitor-sandbox/SKILL.md
  • .agents/skills/nemoclaw-overview/SKILL.md
  • .agents/skills/nemoclaw-overview/references/ecosystem.md
  • .agents/skills/nemoclaw-overview/references/how-it-works.md
  • .agents/skills/nemoclaw-overview/references/overview.md
  • .agents/skills/nemoclaw-overview/references/release-notes.md
  • .agents/skills/nemoclaw-reference/SKILL.md
  • .agents/skills/nemoclaw-reference/references/architecture.md
  • .agents/skills/nemoclaw-reference/references/commands.md
  • .agents/skills/nemoclaw-reference/references/network-policies.md
  • .agents/skills/nemoclaw-reference/references/troubleshooting.md
  • .agents/skills/nemoclaw-workspace/SKILL.md
  • .agents/skills/nemoclaw-workspace/references/workspace-files.md
  • docs/security/best-practices.md
  • nemoclaw/src/index.ts
  • nemoclaw/src/register.test.ts
  • nemoclaw/src/security/secret-scanner.test.ts
  • nemoclaw/src/security/secret-scanner.ts
✅ Files skipped from review due to trivial changes (20)
  • .agents/skills/nemoclaw-overview/references/release-notes.md
  • .agents/skills/nemoclaw-configure-security/SKILL.md
  • .agents/skills/nemoclaw-reference/SKILL.md
  • docs/security/best-practices.md
  • .agents/skills/nemoclaw-configure-security/references/credential-storage.md
  • .agents/skills/nemoclaw-reference/references/commands.md
  • .agents/skills/nemoclaw-get-started/SKILL.md
  • .agents/skills/nemoclaw-overview/references/ecosystem.md
  • .agents/skills/nemoclaw-configure-inference/references/inference-options.md
  • .agents/skills/nemoclaw-reference/references/network-policies.md
  • .agents/skills/nemoclaw-configure-inference/SKILL.md
  • .agents/skills/nemoclaw-workspace/references/workspace-files.md
  • .agents/skills/nemoclaw-reference/references/architecture.md
  • nemoclaw/src/register.test.ts
  • .agents/skills/nemoclaw-deploy-remote/references/sandbox-hardening.md
  • .agents/skills/nemoclaw-overview/references/how-it-works.md
  • .agents/skills/nemoclaw-monitor-sandbox/SKILL.md
  • nemoclaw/src/security/secret-scanner.test.ts
  • .agents/skills/nemoclaw-deploy-remote/SKILL.md
  • .agents/skills/nemoclaw-manage-policy/SKILL.md

Comment on lines +1 to +3
# Security Best Practices

NemoClaw ships with deny-by-default security controls across four layers: network, filesystem, process, and inference.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add the required SPDX header before the first heading.

This new Markdown file is missing the two HTML-comment SPDX lines required at the top of repo source files. As per coding guidelines, "Every source file must include an SPDX license header in the format: // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. and // SPDX-License-Identifier: Apache-2.0 (or # for shell scripts, HTML comments for Markdown)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/nemoclaw-configure-security/references/best-practices.md
around lines 1 - 3, Add the required SPDX header as two HTML comments
immediately before the first heading ("Security Best Practices") in this
Markdown file; insert the SPDX copyright line and the SPDX-License-Identifier
line in HTML comment form (matching the repo guideline texts) so the file begins
with those two SPDX comment lines followed by the existing "# Security Best
Practices" heading.

@@ -0,0 +1,64 @@
# Overview

NVIDIA NemoClaw is an open source reference stack that simplifies running [OpenClaw](https://openclaw.ai) always-on assistants.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use hyphenated compound adjectives for consistency.

For adjective compounds before nouns, use hyphens (e.g., “open-source”). This improves readability and aligns with standard technical style.

Suggested wording
-NVIDIA NemoClaw is an open source reference stack that simplifies running [OpenClaw](https://openclaw.ai) always-on assistants.
+NVIDIA NemoClaw is an open-source reference stack that simplifies running [OpenClaw](https://openclaw.ai) always-on assistants.
-NemoClaw pairs open source and hosted models (for example [NVIDIA Nemotron](https://build.nvidia.com)) with a hardened sandbox, routed inference, and declarative egress policy so deployment stays safer and more repeatable.
+NemoClaw pairs open-source and hosted models (for example [NVIDIA Nemotron](https://build.nvidia.com)) with a hardened sandbox, routed inference, and declarative egress policy so deployment stays safer and more repeatable.

Also applies to: 8-8

🧰 Tools
🪛 LanguageTool

[grammar] ~3-~3: Use a hyphen to join words.
Context: # Overview NVIDIA NemoClaw is an open source reference stack that simplifies r...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/nemoclaw-overview/references/overview.md at line 3, Summary:
use a hyphenated compound adjective for "open-source". Fix: change the sentence
that begins "NVIDIA NemoClaw is an open source reference stack..." to use
"open-source" (i.e., "NVIDIA NemoClaw is an open-source reference stack that
simplifies running..."); also update the other flagged instance of "open source"
in this file to "open-source" for consistency.

# Overview

NVIDIA NemoClaw is an open source reference stack that simplifies running [OpenClaw](https://openclaw.ai) always-on assistants.
NemoClaw provides onboarding, lifecycle management, and management of OpenClaw within OpenShell containers.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Tighten duplicated wording in the intro sentence.

Line 4 repeats “management” and reads awkwardly. Consider simplifying to avoid redundancy and improve clarity.

Suggested wording
-NemoClaw provides onboarding, lifecycle management, and management of OpenClaw within OpenShell containers.
+NemoClaw provides onboarding, lifecycle management, and OpenClaw operations within OpenShell containers.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
NemoClaw provides onboarding, lifecycle management, and management of OpenClaw within OpenShell containers.
NemoClaw provides onboarding, lifecycle management, and OpenClaw operations within OpenShell containers.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/nemoclaw-overview/references/overview.md at line 4, The intro
sentence "NemoClaw provides onboarding, lifecycle management, and management of
OpenClaw within OpenShell containers." repeats "management" and should be
simplified; replace it with a single clear phrase such as "NemoClaw provides
onboarding and lifecycle management of OpenClaw within OpenShell containers" (or
"provides onboarding, lifecycle, and operational management of OpenClaw..." if
you need three distinct items) by editing the sentence in overview.md to remove
the duplicated word and ensure parallel structure.

Comment on lines +1 to +4
---
name: "nemoclaw-overview"
description: "Explains how OpenClaw, OpenShell, and NemoClaw form the ecosystem, NemoClaw’s position in the stack, and when to prefer NemoClaw versus integrating OpenShell and OpenClaw directly. Use when users ask about the relationship between OpenClaw, OpenShell, and NemoClaw, or when to use NemoClaw versus OpenShell. Describes how NemoClaw works internally: CLI, plugin, blueprint runner, OpenShell orchestration, inference routing, and protection layers. Use for sandbox lifecycle and architecture mechanics; not for product definition (Overview) or multi-project placement (Ecosystem). Explains what NemoClaw covers: onboarding, lifecycle management, and management of OpenClaw within OpenShell containers, plus capabilities and why it exists. Use when users ask what NemoClaw is or what the project provides. For ecosystem placement or OpenShell-only paths, use the Ecosystem page; for internal mechanics, use How It Works. Lists changelogs and feature history for NemoClaw releases. Use when checking what changed in a releas..."
---
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add the required SPDX header before the front matter.

Please prepend the two HTML-comment SPDX lines before the YAML front matter. As per coding guidelines, "Every source file must include an SPDX license header in the format: // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. and // SPDX-License-Identifier: Apache-2.0 (or # for shell scripts, HTML comments for Markdown)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/nemoclaw-overview/SKILL.md around lines 1 - 4, Prepend the
two required SPDX header lines as HTML comments at the very top of SKILL.md
before the YAML front matter: add an HTML-comment line with the
SPDX-FileCopyrightText value "Copyright (c) 2026 NVIDIA CORPORATION &
AFFILIATES. All rights reserved." and a second HTML-comment line with the
SPDX-License-Identifier "Apache-2.0", ensuring they appear before the existing
--- YAML front matter block so the file complies with the SPDX header rule for
Markdown files.

Comment on lines +1 to +5
# Troubleshooting

This page covers common issues you may encounter when installing, onboarding, or running NemoClaw, along with their resolution steps.

> **Get Help:** If your issue is not listed here, join the [NemoClaw Discord channel](https://discord.gg/XFpfPv9Uvx) to ask questions and get help from the community. You can also [file an issue on GitHub](https://github.com/NVIDIA/NemoClaw/issues/new).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add the required SPDX header before the first heading.

This new Markdown file is missing the two HTML-comment SPDX lines required at the top of repo source files. As per coding guidelines, "Every source file must include an SPDX license header in the format: // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. and // SPDX-License-Identifier: Apache-2.0 (or # for shell scripts, HTML comments for Markdown)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/nemoclaw-reference/references/troubleshooting.md around lines
1 - 5, Add the required SPDX header as two HTML comments immediately before the
first heading "# Troubleshooting": insert "<!-- SPDX-FileCopyrightText:
Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -->"
and "<!-- SPDX-License-Identifier: Apache-2.0 -->" at the top of the Markdown
file so the SPDX header precedes the "# Troubleshooting" heading.

Comment on lines +1 to +4
---
name: "nemoclaw-workspace"
description: "Backs up and restores OpenClaw workspace files before destructive operations. Use when backing up a sandbox, restoring workspace state, or preparing for a destructive operation. Explains what workspace files are, where they live, and how they persist across sandbox restarts. Use when asking about soul.md, identity.md, memory.md, agents.md, or sandbox file persistence."
---
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add the required SPDX header before the front matter.

Please prepend the two HTML-comment SPDX lines before the YAML front matter. As per coding guidelines, "Every source file must include an SPDX license header in the format: // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. and // SPDX-License-Identifier: Apache-2.0 (or # for shell scripts, HTML comments for Markdown)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/nemoclaw-workspace/SKILL.md around lines 1 - 4, Add the
required SPDX header as two HTML-comment lines immediately before the YAML front
matter in SKILL.md: prepend the lines for SPDX-FileCopyrightText and
SPDX-License-Identifier (using HTML comments <!-- ... -->) so they appear above
the initial '---' front matter marker in
.agents/skills/nemoclaw-workspace/SKILL.md.

Comment thread nemoclaw/src/index.ts Outdated
Comment thread nemoclaw/src/security/secret-scanner.ts
Comment thread nemoclaw/src/security/secret-scanner.ts
Copy link
Copy Markdown
Contributor

@cv cv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security Review — WARNING

The core design is sound — right hook point (before_tool_call), fail-open, safe redaction, good test coverage. Two issues should be fixed before merge.

Required changes

1. Resolve file paths before checking (MEDIUM):
filePath from event.params is passed directly to isMemoryPath() without resolution. Use api.resolvePath(filePath) first to handle symlinks and traversal. CodeRabbit flagged this too.

2. Tighten path segment matching (MEDIUM):

  • /workspace/ is too broad — matches /sandbox/my-project/workspace/readme.md. Should be anchored to .openclaw-data/workspace/ or .openclaw/workspace/.
  • /MEMORY.md matches any file with that name anywhere. Should be anchored to .openclaw or .openclaw-data directories.

Suggestions (non-blocking)

  • Document known bypass vectors (base64, hex, split writes) in code comments so the team tracks the inherent regex limitations
  • Consider adding case-insensitive flag to AWS secret key and Authorization Bearer patterns
  • The rest is solid — fail-open design, redacted error messages, no config knob, comprehensive tests

prekshivyas and others added 2 commits April 8, 2026 14:50
Add Memory Secret Scanner section to the security best-practices doc
describing the before_tool_call hook, covered paths, and 14 secret
patterns. Also add a contract verification comment in the plugin code
noting the dependency on OpenClaw's hook API shape.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pic overlap

- Use matchAll with global flag to detect all occurrences per pattern,
  not just the first. Deduplicates identical values.
- Exclude sk-ant- prefix from OpenAI pattern via negative lookahead so
  Anthropic keys are only reported once, not double-matched.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@prekshivyas prekshivyas force-pushed the security/block-secret-memory-writes branch from 301c44c to dbc3f9f Compare April 8, 2026 22:18
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
nemoclaw/src/security/secret-scanner.ts (2)

36-39: ⚠️ Potential issue | 🟠 Major

Make the context-anchored secret regexes case-insensitive.

AWS_SECRET_ACCESS_KEY= and authorization: bearer ... both bypass the scanner today because these two patterns are exact-case. That weakens the main block-on-write path for very common secret spellings.

🔐 Narrow fix
   {
     name: "AWS secret key",
-    regex: /(?<=aws_secret_access_key\s*[=:]\s*)[A-Za-z0-9/+=]{40}\b/,
+    regex: /(?<=aws_secret_access_key\s*[=:]\s*)[A-Za-z0-9/+=]{40}\b/i,
   },
@@
   {
     name: "Authorization header",
-    regex: /(?<=(?:Authorization\s*:\s*Bearer|Bearer\s*[=:])\s*["']?)[A-Za-z0-9._~+/=-]{40,}/,
+    regex: /(?<=(?:Authorization\s*:\s*Bearer|Bearer\s*[=:])\s*["']?)[A-Za-z0-9._~+/=-]{40,}/i,
   },

Also applies to: 61-64

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/security/secret-scanner.ts` around lines 36 - 39, The regexes
for context-anchored secrets (e.g., the object with name "AWS secret key" whose
regex is /(?<=aws_secret_access_key\s*[=:]\s*)[A-Za-z0-9/+=]{40}\b/ and the
similar "authorization: bearer" pattern) are currently case-sensitive; update
those regex literals to be case-insensitive by adding the i flag (e.g.,
/(?<=aws_secret_access_key\s*[=:]\s*)[A-Za-z0-9/+=]{40}\b/i) and do the same for
the other context-anchored rules (the ones around the authorization/bearer
pattern and the rule at the 61-64 region) so the scanner matches common
upper/lowercase spellings.

106-120: ⚠️ Potential issue | 🟠 Major

Tighten isMemoryPath() to the actual persistent roots.

The bare "/MEMORY.md" substring overmatches unrelated files, while /.openclaw/workspace/, /.openclaw/agents/, /.openclaw/skills/, and /.openclaw/hooks/ are not protected at all. Right now isMemoryPath("/sandbox/.openclaw/workspace/USER.md") is false, but isMemoryPath("/sandbox/project/docs/MEMORY.md") is true.

📁 Suggested matcher update
 const MEMORY_PATH_SEGMENTS = [
   "/.openclaw-data/memory/",
   "/.openclaw/memory/",
-  "/MEMORY.md",
+  "/.openclaw/MEMORY.md",
+  "/.openclaw-data/MEMORY.md",
+  "/.openclaw/workspace/",
   "/.openclaw-data/workspace/",
+  "/.openclaw/agents/",
   "/.openclaw-data/agents/",
+  "/.openclaw/skills/",
   "/.openclaw-data/skills/",
+  "/.openclaw/hooks/",
   "/.openclaw-data/hooks/",
 ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/security/secret-scanner.ts` around lines 106 - 120, The current
matcher overmatches "/MEMORY.md" and misses root ".openclaw" dirs; update
MEMORY_PATH_SEGMENTS to remove the bare "/MEMORY.md" entry and add the missing
root-scoped segments ("/.openclaw/workspace/", "/.openclaw/agents/",
"/.openclaw/skills/", "/.openclaw/hooks/") and then change
isMemoryPath(filePath) so it checks those segments with filePath.includes(...)
but handles the repository root MEMORY.md as an exact match (e.g. filePath ===
"/MEMORY.md" or normalize and compare to the repo root path) instead of
substring matching. Ensure you modify the MEMORY_PATH_SEGMENTS constant and the
isMemoryPath function accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@nemoclaw/src/security/secret-scanner.ts`:
- Around line 36-39: The regexes for context-anchored secrets (e.g., the object
with name "AWS secret key" whose regex is
/(?<=aws_secret_access_key\s*[=:]\s*)[A-Za-z0-9/+=]{40}\b/ and the similar
"authorization: bearer" pattern) are currently case-sensitive; update those
regex literals to be case-insensitive by adding the i flag (e.g.,
/(?<=aws_secret_access_key\s*[=:]\s*)[A-Za-z0-9/+=]{40}\b/i) and do the same for
the other context-anchored rules (the ones around the authorization/bearer
pattern and the rule at the 61-64 region) so the scanner matches common
upper/lowercase spellings.
- Around line 106-120: The current matcher overmatches "/MEMORY.md" and misses
root ".openclaw" dirs; update MEMORY_PATH_SEGMENTS to remove the bare
"/MEMORY.md" entry and add the missing root-scoped segments
("/.openclaw/workspace/", "/.openclaw/agents/", "/.openclaw/skills/",
"/.openclaw/hooks/") and then change isMemoryPath(filePath) so it checks those
segments with filePath.includes(...) but handles the repository root MEMORY.md
as an exact match (e.g. filePath === "/MEMORY.md" or normalize and compare to
the repo root path) instead of substring matching. Ensure you modify the
MEMORY_PATH_SEGMENTS constant and the isMemoryPath function accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f9334759-e138-44ee-878e-6fb2ca62f628

📥 Commits

Reviewing files that changed from the base of the PR and between 301c44c and dbc3f9f.

📒 Files selected for processing (4)
  • docs/security/best-practices.md
  • nemoclaw/src/index.ts
  • nemoclaw/src/security/secret-scanner.test.ts
  • nemoclaw/src/security/secret-scanner.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/security/best-practices.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • nemoclaw/src/index.ts

@wscurran wscurran added priority: high Important issue that should be resolved in the next release NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI). enhancement: security labels Apr 8, 2026
@prekshivyas prekshivyas requested review from brandonpelfrey and cv April 8, 2026 22:25
…-insensitive regexes

- Resolve file paths via api.resolvePath() before isMemoryPath() check
  to prevent traversal bypasses (e.g. ../../.openclaw-data/memory/)
- Anchor /MEMORY.md and /workspace/ to .openclaw-data/ or .openclaw/
  so unrelated project paths don't trigger the scanner
- Add case-insensitive flag to AWS secret key and Authorization header
  patterns so AWS_SECRET_ACCESS_KEY= and authorization: bearer are caught
- Document known bypass vectors (base64, hex, split writes) in comments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nemoclaw/src/index.ts`:
- Around line 289-300: The code currently casts event.params["file_path"/"path"]
and content fields to string then calls api.resolvePath(filePath) and downstream
scanning; instead, validate types first: check typeof rawPath === "string"
before assigning/using rawPath and only call api.resolvePath when that check
passes, and similarly verify content is a string (e.g., typeof content ===
"string") before using it or calling scanForSecrets; update the logic around the
symbols rawPath, filePath, api.resolvePath, isMemoryPath, and content to
early-return undefined for non-string inputs so malformed hook events cannot
reach resolvePath or secret scanning.

In `@nemoclaw/src/security/secret-scanner.ts`:
- Around line 29-32: The GitHub token pattern in the secret scanner (the entry
with name "GitHub token") only covers legacy prefixes and must be expanded to
also detect fine-grained PATs; update the regex for the "GitHub token" entry in
secret-scanner.ts to include the "github_pat_" prefix (i.e., add github_pat to
the alternation that precedes the underscore) so fine-grained tokens are matched
alongside ghp/gho/ghu/ghs/ghr.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3631b388-8ef3-4087-a439-c836b6a44b3c

📥 Commits

Reviewing files that changed from the base of the PR and between dbc3f9f and 389d51b.

📒 Files selected for processing (3)
  • nemoclaw/src/index.ts
  • nemoclaw/src/security/secret-scanner.test.ts
  • nemoclaw/src/security/secret-scanner.ts

Comment thread nemoclaw/src/index.ts Outdated
Comment thread nemoclaw/src/security/secret-scanner.ts Outdated
prekshivyas and others added 7 commits April 8, 2026 17:46
Add github_pat_ prefix to GitHub token regex for fine-grained PAT
detection, add proper typeof guards for hook params instead of unsafe
as-casts, fix "open source" hyphenation and duplicated "management"
wording across docs, and regenerate skill files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@prekshivyas
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
nemoclaw/src/security/secret-scanner.ts (1)

87-89: Static analysis false positive — patterns are static, not user-controlled.

The ast-grep ReDoS warning flags new RegExp(regex.source, flags) as risky, but this is a false positive since SECRET_PATTERNS is a hardcoded array in the same module, not derived from user input.

That said, recreating RegExp objects on every scanForSecrets() call is slightly wasteful. Consider precompiling the patterns with the g flag at module load time.

♻️ Optional: precompile patterns with global flag
-const SECRET_PATTERNS: SecretPattern[] = [
+const SECRET_PATTERNS: Array<{ name: string; regex: RegExp }> = [
   // NVIDIA
-  { name: "NVIDIA API key", regex: /\bnvapi-[A-Za-z0-9_-]{20,}\b/ },
+  { name: "NVIDIA API key", regex: /\bnvapi-[A-Za-z0-9_-]{20,}\b/g },
   // ... apply /g to all patterns ...
 ];

 export function scanForSecrets(content: string): SecretMatch[] {
   const matches: SecretMatch[] = [];
   const seen = new Set<string>();

   for (const { name, regex } of SECRET_PATTERNS) {
-    const flags = regex.flags.includes("g") ? regex.flags : `${regex.flags}g`;
-    for (const match of content.matchAll(new RegExp(regex.source, flags))) {
+    regex.lastIndex = 0; // reset stateful global regex
+    for (const match of content.matchAll(regex)) {
       const value = match[0];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/security/secret-scanner.ts` around lines 87 - 89,
SECRET_PATTERNS is a static array so you should avoid recreating RegExp
instances in scanForSecrets(); instead precompile each pattern once at module
load with the global flag and use those compiled regexes in scanForSecrets() to
eliminate the new RegExp(regex.source, flags) call and prevent repeated
allocation—ensure the precompilation logic (e.g., build a compiledSecrets array
from SECRET_PATTERNS where each entry has the RegExp with 'g' set) and update
scanForSecrets() to iterate over compiledSecrets and call content.matchAll on
the precompiled regex objects.
nemoclaw/src/index.ts (1)

282-284: Minor: Consider narrowing the type guard for stronger type safety.

The Partial<BeforeToolCallEvent> cast with optional chaining works, but you could strengthen the guard by explicitly checking the shape before narrowing:

if (
  typeof event !== "object" ||
  event === null ||
  typeof event.toolName !== "string" ||
  typeof event.params !== "object"
) {
  return undefined;
}

This is optional — the current approach is functionally safe since you check event?.toolName and event.params before use.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/index.ts` around lines 282 - 284, The current guard in the
api.on("before_tool_call", ...) handler uses a Partial<BeforeToolCallEvent> cast
and optional chaining; replace it with an explicit runtime shape check to narrow
types more safely: verify event is a non-null object, event.toolName is a
string, and event.params is an object before proceeding, so the handler
(returning BeforeToolCallResult | undefined) can use event.toolName and
event.params with stronger type safety in the before_tool_call callback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@nemoclaw/src/index.ts`:
- Around line 282-284: The current guard in the api.on("before_tool_call", ...)
handler uses a Partial<BeforeToolCallEvent> cast and optional chaining; replace
it with an explicit runtime shape check to narrow types more safely: verify
event is a non-null object, event.toolName is a string, and event.params is an
object before proceeding, so the handler (returning BeforeToolCallResult |
undefined) can use event.toolName and event.params with stronger type safety in
the before_tool_call callback.

In `@nemoclaw/src/security/secret-scanner.ts`:
- Around line 87-89: SECRET_PATTERNS is a static array so you should avoid
recreating RegExp instances in scanForSecrets(); instead precompile each pattern
once at module load with the global flag and use those compiled regexes in
scanForSecrets() to eliminate the new RegExp(regex.source, flags) call and
prevent repeated allocation—ensure the precompilation logic (e.g., build a
compiledSecrets array from SECRET_PATTERNS where each entry has the RegExp with
'g' set) and update scanForSecrets() to iterate over compiledSecrets and call
content.matchAll on the precompiled regex objects.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 65f9155c-18b3-4763-9c97-763cedb2f6dd

📥 Commits

Reviewing files that changed from the base of the PR and between 389d51b and 1993295.

📒 Files selected for processing (10)
  • .agents/skills/nemoclaw-user-configure-security/references/best-practices.md
  • .agents/skills/nemoclaw-user-overview/SKILL.md
  • .agents/skills/nemoclaw-user-overview/references/ecosystem.md
  • .agents/skills/nemoclaw-user-overview/references/overview.md
  • docs/about/ecosystem.md
  • docs/about/overview.md
  • docs/index.md
  • docs/security/best-practices.md
  • nemoclaw/src/index.ts
  • nemoclaw/src/security/secret-scanner.ts
✅ Files skipped from review due to trivial changes (8)
  • docs/about/ecosystem.md
  • .agents/skills/nemoclaw-user-overview/references/ecosystem.md
  • docs/index.md
  • .agents/skills/nemoclaw-user-overview/references/overview.md
  • .agents/skills/nemoclaw-user-overview/SKILL.md
  • docs/about/overview.md
  • .agents/skills/nemoclaw-user-configure-security/references/best-practices.md
  • docs/security/best-practices.md

@cv cv removed their request for review April 9, 2026 19:54
@prekshivyas prekshivyas self-assigned this Apr 10, 2026
@brandonpelfrey
Copy link
Copy Markdown
Collaborator

Confirmed: the actual NVIDIA_API_KEY is blocked from being written to persistent memory.

What I ran

  • Reused onboarded sandbox: my-assistant
  • Executed the built PR plugin hook logic with the real NVIDIA_API_KEY from the VM environment against a memory-path write target:
    • target path: /sandbox/.openclaw-data/memory/secrets.md
    • content included the real secret value

Result

  {
    "blocked": true,
    "mentionsPattern": true,
    "mentionsMemoryWriteBlocked": true,
    "leaksFullSecret": false,
    "redactedPreviewPresent": true,
    "warnLogs": [
      "[SECURITY] Blocked memory write to /sandbox/.openclaw-data/memory/secrets.md — secrets detected"
    ],
    "blockReason": "Memory write blocked: detected 1 likely secret(s):\n  - NVIDIA API key (nvap..m3BH)\n\nRemove secrets before saving to persistent memory. Use environment variables or credential
stores instead."
  }

What this proves

  • The write is blocked
  • The detector recognizes the real secret as an NVIDIA API key
  • The full secret is not leaked in the block reason or warning log
  • Only a redacted preview is shown: nvap..m3BH

Conclusion

prekshivyas and others added 3 commits April 11, 2026 11:26
Address red-team finding (Test 6): several persistent data paths were
not covered by isMemoryPath(), allowing secrets to be written to
credentials/, canvas/, identity/, cron/, telegram/, sandbox/, and
.nemoclaw/ without triggering the scanner.

Added paths:
- /.openclaw-data/credentials/
- /.openclaw-data/canvas/
- /.openclaw-data/identity/
- /.openclaw-data/cron/
- /.openclaw-data/telegram/
- /.openclaw-data/sandbox/
- /.nemoclaw/

Added 7 tests covering each new path.

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tions

Address red-team finding (Test 3): openclaw agent --local bypasses
the NemoClaw gateway, skipping secret scanning, network policy, and
inference auth.

- Add runtime warning in the openclaw() shell guard when --local is
  detected (scans all args, does not block — preserves user workflow)
- Document known limitations in docs/security/best-practices.md:
  --local bypass, direct FS writes, encoded secrets
- A future OpenClaw-level hook will close the --local gap (upstream)

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@prekshivyas
Copy link
Copy Markdown
Contributor Author

prekshivyas commented Apr 11, 2026

Red-team findings addressed

Following internal adversarial testing, all findings are now resolved:

Finding Resolution
Path coverage gaps in isMemoryPath() Fixed — expanded to cover all persistent data paths + 7 tests
Application-layer bypass vector Mitigated — runtime warning added, documented in Known Limitations. Full fix tracked upstream.
Direct FS writes bypass scanner Documented — expected behavior, application-layer defense-in-depth
Test reimplemented redaction logic Fixed — tests now call actual redact() functions

Commits

  • d651447 — expand isMemoryPath() coverage
  • 36f11ad — add runtime warning and document limitations
  • 9972e6d — call actual redact() functions in tests

@ericksoa ericksoa merged commit fcb5fd6 into NVIDIA:main Apr 12, 2026
13 checks passed
ericksoa pushed a commit to cheese-head/NemoClaw that referenced this pull request Apr 14, 2026
…VIDIA#1632)

## Summary

- Registers a `before_tool_call` plugin hook that blocks
write/edit/apply_patch/notebook_edit tool calls to persistent memory
paths when secrets are detected
- 14 high-confidence secret patterns: NVIDIA, OpenAI, Anthropic, GitHub,
AWS, Slack, Discord, npm, Telegram, Google, HuggingFace tokens, private
keys (PEM), authorization headers
- Protected paths: `.openclaw-data/memory/`, `workspace/`, `agents/`,
`skills/`, `hooks/`, `MEMORY.md`
- Hook registration wrapped in try/catch so scanner failures cannot
break plugin startup
- Non-memory paths and clean content pass through unchanged

Closes NVIDIA#1233

## Test plan

- [x] 21 unit tests for secret patterns (true positives, false
positives, redaction, path matching)
- [x] 9 integration tests for hook wiring (blocks secrets in memory,
allows clean content, allows non-memory paths, handles missing events)
- [x] TypeScript build passes
- [x] All 272 plugin tests pass
- [x] All 954 CLI tests pass
- [x] gitleaks + detect-private-key hooks pass (test fixtures use
runtime-assembled strings)
- [x] Manual test: verify blocked write shows actionable error in agent
session

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Automatic secret scanner that blocks write-like operations to
persistent memory/workspace paths when high-confidence credentials are
detected, returns an actionable block reason, and logs a security
warning. Matches are redacted in reports.

* **Documentation**
* Added guidance describing the scanner, covered locations, and
observable blocking behavior.

* **Tests**
* Added tests for secret detection, memory-path checks, blocking
behavior, and warning logs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement: security NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI). priority: high Important issue that should be resolved in the next release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security: block secret writes into persistent workspace memory

5 participants