Skip to content

[Fix] Confine site env path reads and gate raw secrets on write access - #1219

Open
RichardAnderson wants to merge 5 commits into
vitodeploy:4.xfrom
RichardAnderson:fix/1218
Open

[Fix] Confine site env path reads and gate raw secrets on write access#1219
RichardAnderson wants to merge 5 commits into
vitodeploy:4.xfrom
RichardAnderson:fix/1218

Conversation

@RichardAnderson

@RichardAnderson RichardAnderson commented Jul 28, 2026

Copy link
Copy Markdown
Member

Closes #1218 by confining the env query parameter to the site's own directory and shell-escaping it before sudo cat — the unescaped path made this command injection as a root-capable user, not just the reported arbitrary file read - and restricts raw .env content and unmasked secret values to callers with write access on both the web and API endpoints, while adding a write-access-only Classic mode raw editor and revealable saved secrets.

Summary by CodeRabbit

  • New Features
    • Added classic raw-text and structured environment editing, including parse/stringify and “representability” checks when round-tripping values.
    • Added support for revealing raw .env content and real secret values to authorised users.
  • Security & Permissions
    • Read-only access now consistently masks/removes raw .env and secret values; reveal/edit controls honour revealability.
    • Added stricter validation for environment file path overrides.
  • Bug Fixes
    • Improved handling of quoted and escaped values, plus safer remote env file write and cleanup.
  • Documentation
    • Updated the OpenAPI contract for env GET/PUT, including masked/omitted secrets and error responses.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Environment retrieval and editing now support validated paths, permission-based secret disclosure, raw and structured modes, improved parsing, safer SSH commands, stable variable rows, and expanded API and test coverage.

Changes

Environment access and editing

Layer / File(s) Summary
Path validation and access control
app/Actions/Site/GetEnv.php, app/Http/Controllers/..., app/Models/Site.php, app/Policies/SitePolicy.php, resources/views/ssh/os/read-file.blade.php, public/api-docs/openapi/sites.yaml, tests/Feature/..., tests/Unit/SiteEnvPathTest.php
Environment paths are validated and confined to the site, reveal access is permission-controlled, read-only responses mask secrets and omit raw content, and related endpoint behaviour is tested and documented.
Environment parsing and update contracts
app/Actions/Site/ParseEnv.php, app/Actions/Site/StringifyEnv.php, app/Actions/Site/UpdateEnv.php, app/Helpers/EnvParser.php, app/Http/Controllers/ApplicationController.php, tests/Feature/ApplicationTest.php, tests/Unit/EnvParserTest.php
Parsing, stringification, representability checks, raw/structured update handling, and validation are centralised in actions and covered by feature and unit tests.
Application environment editor
resources/js/pages/application/components/env.tsx, resources/js/pages/application/components/env-variable-row.tsx, resources/js/lib/utils.ts, resources/js/types/env.d.ts, resources/js/pages/workers/components/env-dialog.tsx, resources/js/pages/site-settings/components/basic-auth.tsx
The editor supports classic raw text and variables modes, path-aware loading and saving, permission-aware secret controls, shared parsing flows, and stable row identifiers.
SSH command safety and failure handling
app/Helpers/SSH.php, resources/views/ssh/os/*.blade.php, app/SSH/OS/OS.php, app/Support/Testing/SSHFake.php, app/Facades/SSH.php, tests/Unit/...
Remote file writes now quote paths and clean temporary files on both branches, upload permissions are configurable, empty public-key reads raise an error, and SSH command expectations cover failure and quoting behaviour.

Frontend formatting cleanup

Layer / File(s) Summary
Presentation-only formatting
resources/js/pages/backups/*, resources/js/pages/redirects/components/*
Existing JSX and conditional expressions were reformatted without changing behaviour.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: saeedvaziry

Sequence Diagram(s)

sequenceDiagram
  participant Editor
  participant ApplicationController
  participant EnvActions
  participant Site
  participant SSHOS
  Editor->>ApplicationController: request environment path
  ApplicationController->>Site: authorise and resolve path
  ApplicationController->>EnvActions: load, parse, or stringify environment
  EnvActions->>SSHOS: read or write environment file
  SSHOS-->>EnvActions: file content or command result
  EnvActions-->>Editor: masked or editable environment data
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Several unrelated formatting-only edits were added in backups and redirects pages, which are outside the env security scope. Split out or remove the unrelated formatting-only edits so the PR stays focused on env-path confinement and secret visibility.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main security fix: confining env reads and limiting raw secrets to write access.
Linked Issues check ✅ Passed The changes address the issue by confining env paths, escaping shell reads, masking read-only output, and adding regression coverage.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/Actions/Site/UpdateEnv.php (1)

47-56: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Wrap the two database writes in a transaction.

$site->save() and $site->jsonUpdate('type_data', 'env_path', $path) are separate writes; a failure between them leaves the secret-key list updated while env_path still points at the previous file, so subsequent reads and secret restoration target the wrong path. The remote write can't be rolled back, but the DB state should be atomic.

♻️ Proposed fix
-        $site->env_variables = $this->secretKeys($variables);
-        $site->save();
-
-        $site->jsonUpdate('type_data', 'env_path', $path);
+        DB::transaction(function () use ($site, $variables, $path): void {
+            $site->env_variables = $this->secretKeys($variables);
+            $site->save();
+
+            $site->jsonUpdate('type_data', 'env_path', $path);
+        });

As per coding guidelines, "Wrap multi-step mutations in DB::transaction()".

🤖 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 `@app/Actions/Site/UpdateEnv.php` around lines 47 - 56, Wrap the database
mutations in UpdateEnv’s action flow—$site->save() and
$site->jsonUpdate('type_data', 'env_path', $path)—inside a DB::transaction()
callback so env_variables and env_path commit atomically. Keep the remote server
write outside the transaction, and preserve the existing values and ordering
within the transaction.

Source: Coding guidelines

🤖 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 `@app/Actions/Site/ParseEnv.php`:
- Around line 16-18: Update the validation rules in ParseEnv’s Validator::make
call to add a maximum length constraint for content, matching StringifyEnv’s
existing max:1000 limit while preserving the present, nullable, and string
rules.

In `@app/Helpers/SSH.php`:
- Around line 302-305: Update the remote write command in the SSH helper’s
asUser()->exec flow to always remove tmpRemotePath when cat or redirection
fails, while preserving the original command’s exit status. Add a regression
test covering failed writes and verifying temporary-file cleanup.

In `@app/SSH/OS/OS.php`:
- Around line 153-164: Update InstallServer#createUser(), the sole non-test
caller of OS#getPublicKey(), to catch the expected SSHCommandError from
unreadable public keys and mark the installation job as an intended failure.
Preserve the existing failure handling flow and prevent the raw command
exception from being exposed to tenants.

In `@resources/js/pages/application/components/env-variable-row.tsx`:
- Around line 89-96: Update the Input in the environment variable row so the
read-only explanation for isMultiLine is exposed through an always-visible title
or aria-description rather than placeholder text. Preserve the existing readOnly
behavior and retain the normal placeholder for editable values.

In `@resources/js/pages/application/components/env.tsx`:
- Around line 275-287: Extract the repeated parsed-to-variable mapping into a
shared toVariables(parsed) helper near the component utilities, preserving id
generation via rowId(), isSecret from v.is_secret, and isNew: true. Replace the
duplicated parsed.map(...) logic in the upload handler, paste handler, and
switchMode with this helper so all three entry points use the same
transformation.
- Around line 119-149: Move editor state synchronization out of the queryFn in
the application environment query and into an effect driven by query.data (or
derive the values), so cached path switches restore the data for the current
committedPath. Update variables, rawContent, canEdit, and the clean-state flag
from query.data, then include query.isFetching in the busy condition used by the
Save button to prevent saves during refetches.

In `@resources/views/ssh/os/read-file.blade.php`:
- Line 1: The read-file template must enforce shell quoting for every caller. In
resources/views/ssh/os/read-file.blade.php at lines 1-1, apply
escapeshellarg($path) to both path interpolations; in app/SSH/OS/OS.php at lines
258-258, remove call-site escaping and pass the raw path, and make the same
change in getPublicKey() at line 155 so both callers rely on the template.

In `@tests/Unit/SiteEnvPathTest.php`:
- Around line 10-20: Update SiteEnvPathTest to use the repository’s PHPUnit
fixture conventions: include the RefreshDatabase trait and replace the direct
Site construction in the site() helper with Site::factory()->make(...),
preserving the configured path and optional env_path test data.

---

Outside diff comments:
In `@app/Actions/Site/UpdateEnv.php`:
- Around line 47-56: Wrap the database mutations in UpdateEnv’s action
flow—$site->save() and $site->jsonUpdate('type_data', 'env_path', $path)—inside
a DB::transaction() callback so env_variables and env_path commit atomically.
Keep the remote server write outside the transaction, and preserve the existing
values and ordering within the transaction.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: c50f0f2f-929d-45b2-a452-e9b801e5c9d6

📥 Commits

Reviewing files that changed from the base of the PR and between 566ad13 and 69a3bb5.

📒 Files selected for processing (30)
  • app/Actions/Site/GetEnv.php
  • app/Actions/Site/ParseEnv.php
  • app/Actions/Site/StringifyEnv.php
  • app/Actions/Site/UpdateEnv.php
  • app/Helpers/EnvParser.php
  • app/Helpers/SSH.php
  • app/Http/Controllers/API/SiteController.php
  • app/Http/Controllers/ApplicationController.php
  • app/Models/Site.php
  • app/Policies/SitePolicy.php
  • app/SSH/OS/OS.php
  • app/Support/Testing/SSHFake.php
  • public/api-docs/openapi/sites.yaml
  • resources/js/lib/utils.ts
  • resources/js/pages/application/components/env-variable-row.tsx
  • resources/js/pages/application/components/env.tsx
  • resources/js/pages/backups/files.tsx
  • resources/js/pages/backups/index.tsx
  • resources/js/pages/redirects/components/edit-redirect.tsx
  • resources/js/pages/redirects/components/redirect-form-fields.tsx
  • resources/js/pages/site-settings/components/basic-auth.tsx
  • resources/js/pages/workers/components/env-dialog.tsx
  • resources/js/types/env.d.ts
  • resources/views/ssh/os/read-file.blade.php
  • tests/Feature/API/SitesTest.php
  • tests/Feature/ApplicationTest.php
  • tests/Unit/EnvParserTest.php
  • tests/Unit/Jobs/UpdateVitoAgentConfigJobTest.php
  • tests/Unit/SSH/Services/Webserver/DeploySplashTest.php
  • tests/Unit/SiteEnvPathTest.php

Comment thread app/Actions/Site/ParseEnv.php
Comment thread app/Helpers/SSH.php
Comment thread app/SSH/OS/OS.php
Comment thread resources/js/pages/application/components/env-variable-row.tsx
Comment thread resources/js/pages/application/components/env.tsx
Comment thread resources/js/pages/application/components/env.tsx
Comment thread resources/views/ssh/os/read-file.blade.php Outdated
Comment thread tests/Unit/SiteEnvPathTest.php Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/Actions/Site/UpdateEnv.php (1)

34-35: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Reject an explicit null raw environment value.

{"env": null} makes $hasEnv true when variables is absent, then Lines 49 and 53 overwrite the remote file with an empty string and clear stored secret keys. Treat only a non-null env as supplied; retain '' as the intentional “clear file” value.

Proposed fix
-        $hasEnv = array_key_exists('env', $input)
-            && ($input['env'] !== null || ! array_key_exists('variables', $input));
+        $hasEnv = array_key_exists('env', $input)
+            && $input['env'] !== null;
🤖 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 `@app/Actions/Site/UpdateEnv.php` around lines 34 - 35, The $hasEnv condition
in UpdateEnv must treat an explicit null env as absent while preserving an empty
string as supplied; require the env value itself to be non-null and retain the
existing variables-based handling so null does not overwrite the remote file or
clear stored secret keys.
🤖 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 `@resources/js/pages/application/components/env.tsx`:
- Around line 475-481: Update the Save control’s disabled condition in the form
component to include `canEdit !== true`, ensuring read-only users cannot
initiate the mutation even when the response is non-empty. Preserve all existing
processing, busy, error, path, duplicate, and empty-variable checks.

In `@resources/views/ssh/os/write-file.blade.php`:
- Around line 1-6: Update the shell flow in the write-file template so cat
writes to a unique temporary file beside the destination rather than directly to
the destination; rename that temporary file to the destination only after cat
succeeds, and clean up both temporary files on success or failure while
preserving the existing destination when copying fails.

In `@tests/Unit/Helpers/SSHTest.php`:
- Around line 13-22: Update
test_write_removes_the_temporary_file_on_both_the_success_and_failure_branches
to configure SSH::fake or the rendered script execution so the remote cat
command fails, then assert that the write operation propagates the exception and
removes the temporary file. Keep the existing success-path coverage while adding
assertions that verify actual failure cleanup rather than only inspecting
generated script text.

---

Outside diff comments:
In `@app/Actions/Site/UpdateEnv.php`:
- Around line 34-35: The $hasEnv condition in UpdateEnv must treat an explicit
null env as absent while preserving an empty string as supplied; require the env
value itself to be non-null and retain the existing variables-based handling so
null does not overwrite the remote file or clear stored secret keys.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: c77b174c-13c5-470f-aff0-1a2ddb76b766

📥 Commits

Reviewing files that changed from the base of the PR and between 69a3bb5 and b61cfd5.

📒 Files selected for processing (10)
  • app/Actions/Site/UpdateEnv.php
  • app/Helpers/SSH.php
  • app/SSH/OS/OS.php
  • resources/js/pages/application/components/env-variable-row.tsx
  • resources/js/pages/application/components/env.tsx
  • resources/views/ssh/os/read-file.blade.php
  • resources/views/ssh/os/write-file.blade.php
  • tests/Unit/Helpers/SSHTest.php
  • tests/Unit/Jobs/UpdateVitoAgentConfigJobTest.php
  • tests/Unit/SiteEnvPathTest.php

Comment thread resources/js/pages/application/components/env.tsx
Comment thread resources/views/ssh/os/write-file.blade.php
Comment thread tests/Unit/Helpers/SSHTest.php Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/Unit/Helpers/SSHTest.php (1)

54-61: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Cover quote and shell-metacharacter paths.

This regression test only exercises a space. Add a destination containing at least a single quote plus characters such as ;, $() or backticks, then assert the complete escaped shell argument. This is required by the PR objective to cover shell-significant input, not just whitespace.

Suggested test case
-        $this->server->ssh()->write('/home/vito/legacy path/.env', 'APP_NAME=TestApp', 'vito');
+        $remotePath = "/home/vito/legacy'; touch /tmp/vito-owned `#/.env`";
+        $this->server->ssh()->write($remotePath, 'APP_NAME=TestApp', 'vito');

-        SSH::assertExecutedContains("> '/home/vito/legacy path/.env'");
+        SSH::assertExecutedContains('> '.escapeshellarg($remotePath));
🤖 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 `@tests/Unit/Helpers/SSHTest.php` around lines 54 - 61, Update
test_write_quotes_the_destination_path to use a destination containing a single
quote and shell metacharacters such as ;, $(), or backticks, then assert the
complete correctly escaped shell argument in SSH::assertExecutedContains.
Preserve the existing write invocation and verify the entire destination remains
safely quoted.
🤖 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 `@tests/Unit/Helpers/SSHTest.php`:
- Around line 22-27: Update the SSH test method around the write failure
assertion to use PHPUnit’s expected-exception handling instead of an explicit
try/catch with an inline comment. Preserve the cleanup assertion by keeping it
in a finally block or equivalent teardown path, and ensure the PHP method body
contains no inline comments.

---

Outside diff comments:
In `@tests/Unit/Helpers/SSHTest.php`:
- Around line 54-61: Update test_write_quotes_the_destination_path to use a
destination containing a single quote and shell metacharacters such as ;, $(),
or backticks, then assert the complete correctly escaped shell argument in
SSH::assertExecutedContains. Preserve the existing write invocation and verify
the entire destination remains safely quoted.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: b1b5be18-eb63-41bb-a3d5-515720fc61c9

📥 Commits

Reviewing files that changed from the base of the PR and between b61cfd5 and 8d87fbd.

📒 Files selected for processing (2)
  • resources/js/pages/application/components/env.tsx
  • tests/Unit/Helpers/SSHTest.php

Comment thread tests/Unit/Helpers/SSHTest.php

Copilot AI left a comment

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.

Pull request overview

This PR addresses issue #1218 by hardening site environment file reads/writes against path injection and tightening permissions around raw .env access and secret value visibility across both web and API surfaces.

Changes:

  • Confines caller-supplied env-path overrides to the site directory (with validation) and shell-escapes paths at the SSH command boundary.
  • Restricts raw .env content + unmasked secret values to callers with write access (and write ability for API tokens), adding UI support for classic/raw editing and secret revealability.
  • Improves env parsing/stringifying robustness and extends test coverage for security and edge cases.

Reviewed changes

Copilot reviewed 31 out of 32 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/Unit/SSH/Services/Webserver/DeploySplashTest.php Updates expectations for quoted write redirections.
tests/Unit/SiteEnvPathTest.php Adds unit coverage for env path confinement/validation behavior.
tests/Unit/Jobs/UpdateVitoAgentConfigJobTest.php Updates assertions to match new quoting/cleanup behavior.
tests/Unit/Helpers/SSHTest.php Adds coverage for write failure propagation and temp cleanup.
tests/Unit/EnvParserTest.php Expands coverage for parsing/stringifying and representability checks.
tests/Feature/ApplicationTest.php Adds feature coverage for path override gating and secret reveal rules.
tests/Feature/API/SitesTest.php Verifies env payload differs for read vs write-scoped tokens.
resources/views/ssh/os/write-file.blade.php New SSH template for safer writes + cleanup on both branches.
resources/views/ssh/os/read-file.blade.php Escapes path arguments to prevent command injection.
resources/js/types/env.d.ts Adds stable id for env rows in UI state.
resources/js/pages/workers/components/env-dialog.tsx Uses stable row ids for env-variable rendering.
resources/js/pages/site-settings/components/basic-auth.tsx Reuses shared rowId() utility.
resources/js/pages/redirects/components/redirect-form-fields.tsx Minor formatting-only change.
resources/js/pages/redirects/components/edit-redirect.tsx Minor formatting-only change.
resources/js/pages/backups/index.tsx Minor formatting-only change.
resources/js/pages/backups/files.tsx Minor formatting-only change.
resources/js/pages/application/components/env.tsx Adds classic/raw editor mode, path “commit” semantics, and can-edit gating.
resources/js/pages/application/components/env-variable-row.tsx Adds revealability + safer editing UX for multiline/secret values.
resources/js/lib/utils.ts Introduces shared rowId() helper.
public/api-docs/openapi/sites.yaml Updates OpenAPI to reflect gated raw env + new error response.
app/Support/Testing/SSHFake.php Adds exec-failure simulation for SSH fake.
app/SSH/OS/OS.php Throws on empty public key reads to surface failures.
app/Policies/SitePolicy.php Adds revealEnv policy that also checks token ability.
app/Models/Site.php Adds resolveEnvPath() and routes env reads through it.
app/Http/Controllers/ApplicationController.php Gates env overrides and adds parse/stringify endpoints.
app/Http/Controllers/API/SiteController.php Gates raw env/secret reveal via revealEnv policy.
app/Helpers/SSH.php Switches write to template-based approach with quoting and cleanup.
app/Helpers/EnvParser.php Improves escape handling + adds representability checks.
app/Actions/Site/UpdateEnv.php Validates inputs, resolves confined paths, and supports classic vs variables update flows.
app/Actions/Site/StringifyEnv.php New action to stringify variables into dotenv content.
app/Actions/Site/ParseEnv.php New action to parse dotenv and report representability.
app/Actions/Site/GetEnv.php Returns masked variables by default; raw env only when reveal is allowed.

Comment thread app/Actions/Site/UpdateEnv.php
Comment thread app/Actions/Site/UpdateEnv.php
Comment thread resources/js/pages/application/components/env.tsx
Comment thread app/Helpers/SSH.php

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/Support/Testing/SSHFake.php (1)

97-102: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Make the fake observe the upload permission.

SSHFake::upload() accepts $permission but discards it, so tests cannot detect if the production code stops passing the required 600 mode. Store the value and expose an assertion or getter for it.

🤖 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 `@app/Support/Testing/SSHFake.php` around lines 97 - 102, Update
SSHFake::upload() to retain the passed $permission value, and add a getter or
assertion method exposing the recorded permission so tests can verify uploads
use the required 600 mode.
app/Helpers/SSH.php (1)

260-275: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Validate and shell-escape $permission before interpolation.

Line 260 introduces a free-form permission argument, which is inserted directly into chmod commands at Lines 270 and 275. A value such as 600; <command> would alter the remote shell command. Restrict the value to a valid octal mode and quote it before execution.

Proposed fix
 public function upload(string $local, string $remote, ?string $owner = null, ?string $log = null, ?int $siteId = null, string $permission = '644'): void
 {
+    if (! preg_match('/\A[0-7]{3,4}\z/', $permission)) {
+        throw new \InvalidArgumentException('Invalid file permission.');
+    }
+
+    $permission = escapeshellarg($permission);
🤖 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 `@app/Helpers/SSH.php` around lines 260 - 275, Update SSH::upload to validate
$permission as a valid octal mode and shell-escape it before interpolating it
into the chmod commands executed via $this->exec. Apply the protected value
consistently to both chmod invocations while preserving the existing upload and
ownership behavior.
🤖 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 `@app/Facades/SSH.php`:
- Line 20: Update the `upload` facade method annotation in `SSH` to document a
`void` return type instead of `string`, matching both implementations and the
actual caller contract.

In `@tests/Feature/ApplicationTest.php`:
- Around line 1486-1501: Add a regression test alongside
test_update_env_rejects_a_submission_with_both_keys that submits env as null
with a non-empty variables array, then assert the application.update-env request
succeeds and is not rejected by env validation. Reuse the existing
authentication, SSH fake, server, site, and variable setup while changing only
the payload needed to cover the hasEnv special case.

---

Outside diff comments:
In `@app/Helpers/SSH.php`:
- Around line 260-275: Update SSH::upload to validate $permission as a valid
octal mode and shell-escape it before interpolating it into the chmod commands
executed via $this->exec. Apply the protected value consistently to both chmod
invocations while preserving the existing upload and ownership behavior.

In `@app/Support/Testing/SSHFake.php`:
- Around line 97-102: Update SSHFake::upload() to retain the passed $permission
value, and add a getter or assertion method exposing the recorded permission so
tests can verify uploads use the required 600 mode.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 29a4d396-e593-412c-89a8-02e02208ee96

📥 Commits

Reviewing files that changed from the base of the PR and between 8d87fbd and 9aaa676.

📒 Files selected for processing (5)
  • app/Actions/Site/UpdateEnv.php
  • app/Facades/SSH.php
  • app/Helpers/SSH.php
  • app/Support/Testing/SSHFake.php
  • tests/Feature/ApplicationTest.php

Comment thread app/Facades/SSH.php
Comment thread tests/Feature/ApplicationTest.php
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.

[Security]: Authenticated arbitrary file read through environment path override

3 participants