feat(security): block secret writes into persistent workspace memory#1632
Conversation
…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>
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 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
FAKEobject: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, butnotebook_edittool is not tested.The test suite thoroughly covers
write,edit, andapply_patchtools, but thenotebook_edittool (listed inWRITE_TOOL_NAMESin 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: UnnecessaryString()conversion.
matches.lengthis already a number, and template literals handle number-to-string coercion automatically. The explicitString()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
📒 Files selected for processing (4)
nemoclaw/src/index.tsnemoclaw/src/register.test.tsnemoclaw/src/security/secret-scanner.test.tsnemoclaw/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>
There was a problem hiding this comment.
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
📒 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.mddocs/security/best-practices.mdnemoclaw/src/index.tsnemoclaw/src/register.test.tsnemoclaw/src/security/secret-scanner.test.tsnemoclaw/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
| # Security Best Practices | ||
|
|
||
| NemoClaw ships with deny-by-default security controls across four layers: network, filesystem, process, and inference. |
There was a problem hiding this comment.
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. | |||
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
| 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.
| --- | ||
| 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..." | ||
| --- |
There was a problem hiding this comment.
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.
| # 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). |
There was a problem hiding this comment.
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.
| --- | ||
| 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." | ||
| --- |
There was a problem hiding this comment.
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.
cv
left a comment
There was a problem hiding this comment.
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.mdmatches any file with that name anywhere. Should be anchored to.openclawor.openclaw-datadirectories.
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
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>
301c44c to
dbc3f9f
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (2)
nemoclaw/src/security/secret-scanner.ts (2)
36-39:⚠️ Potential issue | 🟠 MajorMake the context-anchored secret regexes case-insensitive.
AWS_SECRET_ACCESS_KEY=andauthorization: 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 | 🟠 MajorTighten
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 nowisMemoryPath("/sandbox/.openclaw/workspace/USER.md")isfalse, butisMemoryPath("/sandbox/project/docs/MEMORY.md")istrue.📁 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
📒 Files selected for processing (4)
docs/security/best-practices.mdnemoclaw/src/index.tsnemoclaw/src/security/secret-scanner.test.tsnemoclaw/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
…-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>
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
nemoclaw/src/index.tsnemoclaw/src/security/secret-scanner.test.tsnemoclaw/src/security/secret-scanner.ts
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>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
🧹 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 sinceSECRET_PATTERNSis a hardcoded array in the same module, not derived from user input.That said, recreating
RegExpobjects on everyscanForSecrets()call is slightly wasteful. Consider precompiling the patterns with thegflag 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?.toolNameandevent.paramsbefore 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
📒 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.mddocs/about/ecosystem.mddocs/about/overview.mddocs/index.mddocs/security/best-practices.mdnemoclaw/src/index.tsnemoclaw/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
|
Confirmed: the actual NVIDIA_API_KEY is blocked from being written to persistent memory. What I ran
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
Conclusion
|
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>
Red-team findings addressedFollowing internal adversarial testing, all findings are now resolved:
Commits
|
…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>
Summary
before_tool_callplugin hook that blocks write/edit/apply_patch/notebook_edit tool calls to persistent memory paths when secrets are detected.openclaw-data/memory/,workspace/,agents/,skills/,hooks/,MEMORY.mdCloses #1233
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests