Skip to content

fix: address CodeQL findings (file service + VNC secret at rest)#11

Merged
v1k70rk4 merged 1 commit into
masterfrom
fix/codeql-findings
Jun 17, 2026
Merged

fix: address CodeQL findings (file service + VNC secret at rest)#11
v1k70rk4 merged 1 commit into
masterfrom
fix/codeql-findings

Conversation

@v1k70rk4

@v1k70rk4 v1k70rk4 commented Jun 17, 2026

Copy link
Copy Markdown
Owner

Address CodeQL findings

Triage of the 15 code-scanning alerts: real fixes here, by-design/false-positive ones dismissed with rationale.

Fixed

  • cs/web/xss + info-leak (FileService) — on error, log the detail server-side and return a generic text/plain body instead of echoing ex.Message. No request-controlled data is reflected.
  • cs/log-forging (FileService) — strip CR/LF from the request-controlled path before it enters the audit log.
  • cs/cleartext-storage ×5 (VncProvisioningService) — encrypt vnc.secret at rest with DPAPI (machine scope; the agent runs as SYSTEM), consistent with how the client PFX is protected. Legacy plaintext files migrate transparently on first read, so existing devices keep working.

Dismissed (by design / interop)

  • cs/path-injection ×7 (FileService) — it's a file manager: full-filesystem access is the feature, gated by a per-session token + loopback-only binding + the authenticated reverse tunnel + operator grant + consent (token check precedes all path use). No base dir to confine to.
  • cs/ecb-encryption ×1 (VncPassword) — VNC fixed-key DES is TightVNC's password interop format, not a protection mechanism; the real boundary is the SSH tunnel + loopback.

Agent bumped to 1.7.1. Build only, no rollout yet.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes - v1.7.1.0

  • Security
    • VNC passwords are now encrypted at rest.
    • Audit logging is hardened against injection attacks.
    • Error messages from failed operations now display a generic message instead of exposing internal details.

- FileService: on error, log the detail server-side and return a generic text/plain
  body instead of echoing the exception message (cs/web/xss in context + no info
  leak). Strip CR/LF from audited paths (cs/log-forging).
- VncProvisioningService: encrypt vnc.secret at rest with DPAPI (machine scope; the
  agent runs as SYSTEM), migrating legacy plaintext files transparently
  (cs/cleartext-storage).

Remaining CodeQL alerts are by design and dismissed separately: full-filesystem
path access in the authenticated, token-gated, loopback file manager; and the
fixed-key DES of the VNC password interop format.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Three security-focused changes in RemoteAgent: FileService now returns a generic "Operation failed." HTTP 500 body instead of the raw exception message, and its audit logger strips CR/LF from detail strings. VncProvisioningService replaces plaintext vnc.secret read/write with DPAPI-encrypted ReadSecret/WriteSecret helpers that also transparently migrate legacy plaintext files. Version bumped to 1.7.1.0.

Changes

FileService Security Hardening

Layer / File(s) Summary
Generic error response and audit log injection fix
src/RemoteAgent/Files/FileService.cs
Exception handler returns fixed "Operation failed." with HTTP 500 instead of ex.Message; Audit method replaces \r/\n in user-controlled detail with spaces before writing to the log.

VNC DPAPI Encryption

Layer / File(s) Summary
DPAPI secret helpers and provisioning wiring
src/RemoteAgent/Services/VncProvisioningService.cs, src/RemoteAgent/RemoteAgent.csproj
Adds System.Security.Cryptography import; startup provisioning and the watchdog loop use new ReadSecret/WriteSecret helpers backed by ProtectedData.Protect/Unprotect (machine scope) with best-effort migration of legacy plaintext files on first read; version bumped to 1.7.1.0.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 A bunny once stored a VNC key,
In plaintext for all who might see—
Now DPAPI shields it tight,
Log-injection? Stripped from sight!
And errors stay server-side, safe with me. 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the changeset: addressing CodeQL security findings in two key components (file service and VNC secret storage).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/codeql-findings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Comment thread src/RemoteAgent/Files/FileService.cs Dismissed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/RemoteAgent/Services/VncProvisioningService.cs (1)

107-117: ⚡ Quick win

Prefer the shared RemoteAgent.Security.Dpapi wrapper instead of direct ProtectedData calls.

This keeps crypto usage consistent with the existing repo abstraction and prevents drift in scope/entropy handling across files.

🤖 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/RemoteAgent/Services/VncProvisioningService.cs` around lines 107 - 117,
Replace the direct ProtectedData.Unprotect call in the try block and the
ProtectedData.Protect call in the WriteSecret method with the shared
RemoteAgent.Security.Dpapi wrapper instead. This ensures consistent crypto
handling across the codebase and prevents potential drift in scope and entropy
handling. Use the appropriate Dpapi wrapper methods for both the unprotect
operation and the protect operation in WriteSecret.
🤖 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/RemoteAgent/Services/VncProvisioningService.cs`:
- Around line 107-113: The ReadSecret method currently treats any
ProtectedData.Unprotect failure as legacy plaintext, which can silently corrupt
encrypted blobs that are tampered or corrupted by decoding them as text and
re-writing them. Instead of catching all exceptions from Unprotect, validate
that the raw data actually represents valid plaintext before treating it as
legacy; consider checking if the data decodes to reasonable text content or if
there are specific indicators that distinguish legitimate legacy plaintext from
corrupted encrypted data, and only fall back to plaintext handling when you are
confident the data is not a corrupted encrypted blob.

---

Nitpick comments:
In `@src/RemoteAgent/Services/VncProvisioningService.cs`:
- Around line 107-117: Replace the direct ProtectedData.Unprotect call in the
try block and the ProtectedData.Protect call in the WriteSecret method with the
shared RemoteAgent.Security.Dpapi wrapper instead. This ensures consistent
crypto handling across the codebase and prevents potential drift in scope and
entropy handling. Use the appropriate Dpapi wrapper methods for both the
unprotect operation and the protect operation in WriteSecret.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: a9721e5f-6142-4cdd-8e1f-61878267b60a

📥 Commits

Reviewing files that changed from the base of the PR and between 86dff76 and 8199772.

📒 Files selected for processing (3)
  • src/RemoteAgent/Files/FileService.cs
  • src/RemoteAgent/RemoteAgent.csproj
  • src/RemoteAgent/Services/VncProvisioningService.cs

Comment on lines +107 to +113
try { return Encoding.UTF8.GetString(ProtectedData.Unprotect(raw, null, DataProtectionScope.LocalMachine)).Trim(); }
catch
{
var plain = Encoding.UTF8.GetString(raw).Trim(); // legacy plaintext
try { WriteSecret(file, plain); } catch { /* best-effort migration */ }
return plain;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Differentiate legacy plaintext from decryption failure to avoid silent secret corruption.

ReadSecret currently treats any Unprotect failure as legacy plaintext. If an encrypted blob is corrupted/tampered, it gets decoded as text, re-written, and used as the active password. That can silently rotate to garbage and break the persisted secret contract.

Proposed hardening
+    private static ReadOnlySpan<byte> SecretHeader => "DPAPI1:"u8;
+
     private static string? ReadSecret(string file)
     {
         if (!File.Exists(file)) return null;
         var raw = File.ReadAllBytes(file);
         if (raw.Length == 0) return null;
-        try { return Encoding.UTF8.GetString(ProtectedData.Unprotect(raw, null, DataProtectionScope.LocalMachine)).Trim(); }
-        catch
+        // New encrypted format: header + DPAPI blob
+        if (raw.AsSpan().StartsWith(SecretHeader))
         {
-            var plain = Encoding.UTF8.GetString(raw).Trim();   // legacy plaintext
-            try { WriteSecret(file, plain); } catch { /* best-effort migration */ }
-            return plain;
+            var blob = raw.AsSpan(SecretHeader.Length).ToArray();
+            return Encoding.UTF8.GetString(
+                ProtectedData.Unprotect(blob, null, DataProtectionScope.LocalMachine)).Trim();
         }
+
+        // Legacy plaintext migration path only.
+        var plain = Encoding.UTF8.GetString(raw).Trim();
+        try { WriteSecret(file, plain); } catch { /* best-effort migration */ }
+        return plain;
     }
 
     private static void WriteSecret(string file, string password) =>
-        File.WriteAllBytes(file, ProtectedData.Protect(Encoding.UTF8.GetBytes(password), null, DataProtectionScope.LocalMachine));
+        File.WriteAllBytes(
+            file,
+            SecretHeader.ToArray()
+                .Concat(ProtectedData.Protect(Encoding.UTF8.GetBytes(password), null, DataProtectionScope.LocalMachine))
+                .ToArray());
🤖 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/RemoteAgent/Services/VncProvisioningService.cs` around lines 107 - 113,
The ReadSecret method currently treats any ProtectedData.Unprotect failure as
legacy plaintext, which can silently corrupt encrypted blobs that are tampered
or corrupted by decoding them as text and re-writing them. Instead of catching
all exceptions from Unprotect, validate that the raw data actually represents
valid plaintext before treating it as legacy; consider checking if the data
decodes to reasonable text content or if there are specific indicators that
distinguish legitimate legacy plaintext from corrupted encrypted data, and only
fall back to plaintext handling when you are confident the data is not a
corrupted encrypted blob.

@v1k70rk4 v1k70rk4 merged commit bd4e679 into master Jun 17, 2026
6 of 7 checks passed
@v1k70rk4 v1k70rk4 deleted the fix/codeql-findings branch June 17, 2026 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants