fix(tunnel): verify process identity before stopping cloudflared - #7644
Conversation
stopService signalled whatever PID was recorded in cloudflared.pid after only an isAlive check. If the recorded cloudflared had exited and the OS recycled its PID to an unrelated process, that process was SIGTERM/ SIGKILLed. cloudflared is spawned detached and can exit early (for example on an invalid tunnel token), leaving a stale pid file, so PID reuse is a realistic path, and under an elevated topology the recycled PID can be a system process. Every other kill site in this module already gates on a command-line identity match; stopService was the lone unverified killer. Verify the live PID still names cloudflared (the same readProcessCommandLine plus commandLineNamesCloudflared check readCloudflaredState uses) before signalling, and drop the stale pid file when it does not. A null or unreadable cmdline stays conservative and proceeds, matching readCloudflaredState. Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
📝 WalkthroughWalkthroughChangesPID termination safety
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/pluginThe overall coverage in commit 5700f6a in the TypeScript / code-coverage/cliThe overall coverage in commit 5700f6a in the Show a code coverage summary of the most impacted files.
Updated |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/tunnel/services.test.ts`:
- Around line 494-513: Update the test around stopAll, the /proc state
inspection, and its assertions to use try/finally cleanup. Ensure
logSpy.mockRestore() and the bystander process termination via
process.kill(bpid, "SIGKILL") run in finally blocks even when stopAll,
inspection, or assertions throw, while preserving the existing process-state
checks.
- Around line 482-517: Replace the real sleep process, /proc inspection, and
process.kill cleanup in the test “does not signal a recycled PID whose process
is not cloudflared” with the tunnel adapter/fake pattern used by the surrounding
tests. Configure the fake process lookup to model a recycled PID belonging to a
non-cloudflared process, assert that stopAll does not signal it, and retain the
PID-file removal assertion without broad host interactions.
In `@src/lib/tunnel/services.ts`:
- Around line 352-361: Revalidate PID ownership immediately before the SIGKILL
escalation in the tunnel shutdown flow, using readProcessCommandLine and
commandLineNamesCloudflared as in the existing pre-SIGTERM check. If the live
PID no longer identifies cloudflared, log that it is not running, remove the
stale PID via removePid, and return without sending SIGKILL.
🪄 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: Enterprise
Run ID: e0f44edf-f2ab-4f39-beaf-8f8056a51fda
📒 Files selected for processing (2)
src/lib/tunnel/services.test.tssrc/lib/tunnel/services.ts
PR Review Advisor — No blocking findings reportedAdvisor assessment: No blocking advisor findings reported Model lanes
Nemotron output stays in workflow artifacts and does not change the assessment above. E2E guidanceAdvisory only. E2E / PR Gate selects and runs jobs independently. Recommended E2E: None 1 optional E2E recommendation
This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge. |
…rocess ops in tests Address review feedback on the recycled-PID stop fix. Re-run the command-line identity check immediately before the SIGKILL escalation, not only before SIGTERM: cloudflared can exit and have its PID recycled during the up-to-3s exit poll, so the escalation could otherwise kill an unrelated process. Route stopService process operations (isAlive / commandLine / signal) through an injectable ProcessControl. The regression tests now drive a deterministic fake that models PID reuse instead of spawning a real process and reading /proc/<pid>/stat, which was not portable to the macOS Vitest runner and did not follow the tunnel adapter/fake test convention. Adds coverage for both the recycled-before-SIGTERM and recycled-during-poll paths. Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/lib/tunnel/services.ts (1)
146-162: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider consolidating the duplicated identity-check logic.
pidIsOurs(new) and the inline check inreadCloudflaredState(Line 283:cmdline !== null && !commandLineNamesCloudflared(cmdline)) both implement the same "null-or-cloudflared" rule, butreadCloudflaredStatestill callsreadProcessCommandLine/process.killdirectly instead of going throughProcessControl. Extracting a shared predicate (or havingreadCloudflaredStateaccept an injectableProcessControl) would avoid the duplication and let that function benefit from the same deterministic test seam.As per path instructions (
src/lib/README.md), "align with the adapter/injection pattern where process/FS interactions are involved."Also applies to: 282-285, 364-367
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/tunnel/services.ts` around lines 146 - 162, Consolidate the duplicated PID identity check by reusing pidIsOurs from readCloudflaredState and related lifecycle paths. Route command-line lookup and signaling through the injected ProcessControl interface instead of calling readProcessCommandLine or process.kill directly, preserving the null-or-cloudflared rule and enabling deterministic tests.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/lib/tunnel/services.ts`:
- Around line 146-162: Consolidate the duplicated PID identity check by reusing
pidIsOurs from readCloudflaredState and related lifecycle paths. Route
command-line lookup and signaling through the injected ProcessControl interface
instead of calling readProcessCommandLine or process.kill directly, preserving
the null-or-cloudflared rule and enabling deterministic tests.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 160ce322-dbab-4ff4-8209-8113a41afcaa
📒 Files selected for processing (2)
src/lib/tunnel/services.test.tssrc/lib/tunnel/services.ts
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
|
I reviewed the current exact two-file diff. The identity checks are scoped to accepted issue #7643, and the fail-open behavior for an unreadable command line matches that issue’s explicit compatibility boundary. I’m keeping this as a comment, not a changes-requested review. Two mechanical items remain before exact-head approval:
Once that quiet refreshed head is available, I’ll babysit its focused test, current CI, signatures, and final approval. |
cjagwani
left a comment
There was a problem hiding this comment.
Exact-head approval for f350d4b after the one-hour quiet check. All 50 GitHub checks are green; the focused tunnel suite passes 37/37 locally; CLI typecheck passes with the PR-pinned dependency tree; all seven commits are Verified; and there are no unresolved major findings. Security review PASS across credentials, input handling, authorization, dependencies, error exposure, data protection, configuration, tests, and system/process safety. The stop path now validates cloudflared identity before SIGTERM and revalidates before SIGKILL, with deterministic recycled-PID regressions. The conservative unreadable-command-line behavior is the explicit accepted #7643 contract. GitHub reports no conflict, and the user waiver applies to base-only refreshes.
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
<!-- markdownlint-disable MD041 --> ## Summary Adds the canonical dated changelog entry for `v0.0.100` so the maintainer release plan can verify the pre-tag documentation prerequisite. The entry summarizes the user-facing changes merged since `v0.0.99` and links to the relevant guides. ## Changes - Add `docs/changelog/2026-07-31.mdx` with the exact `## v0.0.100` heading. - Cover restored OpenClaw pairing, transactional replacement, Deep Agents Code, onboarding recovery, lifecycle cleanup, Hermes builds, host provenance, documentation, and trusted E2E evidence. - Distinguish active Docker and Kubernetes runtime-bundle enforcement from the still-inactive managed shared-state transaction foundation. ## Source Coverage The release entry maps the doc-impacting merged PRs in the `v0.0.99..main` release range to `docs/changelog/2026-07-31.mdx`: #8021, #8024, #7973, #8028, #7947, #7788, #7884, #8023, #7969, #8020, #7989, #8000, #7907, #7942, #7567, #8013, #7955, #8017, #8014, #8015, #7629, #7644, #7821, #7971, and #7991. PR #7974 was reviewed after the final rebase and excluded because it changes internal maintainer-skill attribution policy and tests only; it does not change a user-facing product or documentation surface. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [x] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [ ] Tests added or updated for changed behavior - [x] Existing tests cover changed behavior — justification: the changelog contract test validates the dated entry, version heading, SPDX form, and route constraints. - [ ] Tests not applicable — justification: - [x] Docs updated for user-facing behavior changes - [ ] Docs not applicable — justification: - [ ] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [ ] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Documentation Writer Review - [x] Documentation writer subagent reviewed the completed changes - Result: `docs-updated` - Evidence: `docs/changelog/2026-07-31.mdx`; exact-head review passed for `6093f44f`; writing rules and documentation style reviewed; `npx vitest run test/changelog-docs.test.ts` passed 6/6; `npm run docs` passed with zero Fern errors and two generic Fern upgrade notices. - Agent: Codex Desktop <!-- docs-review-head-sha: 6093f44 --> <!-- docs-review-agents-blob-sha: 3dd7c24 --> ## DGX Station Hardware Evidence - [ ] Tested on DGX Station - Tested commit: Not applicable; no DGX Station host script changed. - Station profile/scenario: Not applicable. - Result: Not applicable. - Supporting evidence: Not applicable. ## Verification - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run validate:pr` passed after refreshing `origin/main` when hooks were skipped or unavailable - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — command/result or justification: `npx vitest run test/changelog-docs.test.ts` passed 6/6 at `6093f44f`. - [ ] Applicable broad gate passed — `npm test` for broad runtime/test-harness changes; `npm run check` for repo-wide validation/coverage changes — command/result: Not applicable to a dated prose-only release entry. - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) — validation passed with zero errors; Fern emitted two generic upgrade notices. - [x] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) — the changelog entry has the required parser-safe MDX SPDX header; dated changelog entries intentionally do not use page frontmatter. --- Signed-off-by: Senthil Ravichandran <senthilr@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added release notes for v0.0.100. * Documented improvements to restore pairing, sandbox replacement, onboarding recovery, lifecycle cleanup, runtime handling, build support, host readiness, and end-to-end validation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Senthil Ravichandran <senthilr@nvidia.com>
Summary
stopServicesignalled whatever PID was recorded incloudflared.pidafter only anisAlivecheck. If the recorded cloudflared had exited and the OS recycled its PID to an unrelated process, that process could receive SIGTERM or SIGKILL. This verifies the live PID still names cloudflared before either signal and removes a stale PID file without signalling a bystander.Related Issue
Closes #7643
Changes
SIGTERM→SIGKILL) and PID-file cleanup.Type of Change
Quality Gates
5700f6a22d2dd791b63bba7ddd7cf460800f1299on current base94c0b07efb7bcc5d9ddd1fd590a2da26514bbcec. Categories 1–8 PASS. Category 9 has a non-blocking residual warning: command-line identity lookup and POSIX signalling are not one OS-atomic process-handle operation. This is the explicit conservative compatibility boundary accepted by cloudflared stop can SIGKILL an unrelated process when the recorded PID has been recycled #7643; the exact Advisor reports no blockers, CodeRabbit’s actionable threads are resolved, and the deterministic tests cover a pre-signal bystander plus recycling before escalation. Exact diff fingerprint:44af53152b9cd4b61a10557334ac4bea0a101a6288f19c932e24eef295d450bc(git diff --binary origin/main...HEAD | shasum -a 256).Documentation Writer Review
no-docs-needed5700f6a22d2dd791b63bba7ddd7cf460800f1299changes internal tunnel process-lifecycle safety, regression coverage, and a typed readiness test fixture. User-facing commands, configuration, supported platforms, and documented tunnel behavior are unchanged. Focused tunnel tests pass 38/38, the readiness fixture passes 2/2, and CLI build/typecheck plus normal commit/pre-push hooks pass.DGX Station Hardware Evidence
Verification
Signed-off-by:line and every commit appears asVerifiedin GitHubpre-commit,commit-msg, andpre-pushhooks passed5700f6a22.npm run docsbuilds without warnings (doc changes only)Signed-off-by: latenighthackathon latenighthackathon@users.noreply.github.com
Summary by CodeRabbit