Skip to content

Resolve comprehensive TorBox Media Server code review findings#10

Merged
nordicnode merged 1 commit into
mainfrom
fix-all-reported-issues-7776805136999684067
Apr 29, 2026
Merged

Resolve comprehensive TorBox Media Server code review findings#10
nordicnode merged 1 commit into
mainfrom
fix-all-reported-issues-7776805136999684067

Conversation

@nordicnode
Copy link
Copy Markdown
Owner

@nordicnode nordicnode commented Apr 29, 2026

This submission addresses a large batch of 16 critical, high-impact, and lower-priority bugs identified during a code review of the TorBox-Media-Server repository. It resolves a blocking syntax error in setup.sh that broke all setups, implements crucial missing logic blocks for authentication handling, bolsters CI by correctly propagating syntax failures, sets safer default network bindings for Plex and Jellyfin, and fixes an uninitialized variable that would crash installations if service startup was delayed.


PR created automatically by Jules for task 7776805136999684067 started by @nordicnode


Open in Devin Review

- Fix `configure_arr_auth` syntax error and auth logic skip for fresh installs
- Fix admin credential variable naming and `SERVICES_STARTED` uninitialized error
- Preserve Decypharr password on re-runs and validate `TORBOX_MEDIA_SERVER` input
- Ensure `manage.sh` CI validation fails on syntax errors and test `setup.sh` safely by properly parsing functions
- Add `.env.example` and validate `docker-compose.yml` with representative variables in CI
- Set `manage.sh keys` to mask secrets by default unless `--show-secrets` is passed
- Update `README.md` to reflect pre-seeded credentials and security adjustments
- Bind Plex and Jellyfin default ports to `127.0.0.1` for increased security
- Add fallback docker wrapper in `uninstall.sh` to use `sudo docker` when appropriate
- Increase CI dependency install robustness with `apt-get update`
- Use `docker-compose-plugin` on Fedora instead of the legacy `docker-compose`

Co-authored-by: nordicnode <128633122+nordicnode@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Fix critical bugs and enhance security with credential masking and port binding

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Fix critical syntax error in configure_arr_auth function blocking authentication setup
• Initialize SERVICES_STARTED variable and preserve Decypharr credentials on re-runs
• Implement secure secret masking in manage.sh keys command with --show-secrets flag
• Bind Plex and Jellyfin ports to 127.0.0.1 for enhanced security by default
• Add .env.example file and improve CI validation with docker-compose syntax checking
• Fix docker command detection in uninstall.sh to support both direct and sudo execution
• Enhance CI robustness with apt-get update and proper heredoc parsing for manage.sh validation
• Update documentation to reflect auto-generated credentials and security improvements
Diagram
flowchart LR
  A["setup.sh"] -->|Initialize SERVICES_STARTED| B["Variable Management"]
  A -->|Fix configure_arr_auth logic| C["Authentication Setup"]
  A -->|Preserve Decypharr credentials| D["Credential Persistence"]
  A -->|Add secret masking function| E["manage.sh keys command"]
  A -->|Bind to 127.0.0.1| F["docker-compose.yml"]
  G[".env.example"] -->|Template variables| H["CI Validation"]
  I["uninstall.sh"] -->|Add docker wrapper| J["Docker Command Detection"]
  K["lint.yml"] -->|Enhanced checks| L["CI Pipeline"]
  L -->|Validate compose config| M["docker-compose.yml"]
  N["README.md"] -->|Update security notes| O["Documentation"]
Loading

Grey Divider

File Changes

1. setup.sh 🐞 Bug fix +74/-34

Fix auth logic, initialize variables, add secret masking

• Initialize SERVICES_STARTED=false at script start to prevent uninitialized variable errors
• Fix configure_arr_auth function logic to properly check for existing Forms authentication with
 Required status and username validation
• Preserve Decypharr password on re-runs by checking if already set before generating new one
• Add validation for media server choice with fallback to plex if invalid
• Implement secret masking in manage.sh keys command with mask_val() function and
 --show-secrets flag
• Update admin credential variable naming from env_key to env_key_prefix for clarity
• Extract and preserve existing Decypharr credentials during installation checks
• Set SERVICES_STARTED=false explicitly when services are not started

setup.sh


2. tests/test_utils.sh 🧪 Tests +11/-32

Source test functions from setup.sh directly

• Remove duplicate generate_api_key() and mask_key() function implementations
• Source functions directly from setup.sh using sed extraction to ensure test consistency with
 implementation
• Add error handling for missing setup.sh file
• Simplify test utilities by eliminating code duplication

tests/test_utils.sh


3. uninstall.sh ✨ Enhancement +14/-4

Add docker command wrapper for sudo compatibility

• Add DOCKER_CMD array variable alongside COMPOSE_CMD for flexible docker command execution
• Implement detect_compose_cmd() to set both docker and compose commands with proper sudo handling
• Replace hardcoded docker calls with "${DOCKER_CMD[@]}" array expansion throughout script
• Add docker command detection before container removal and image cleanup operations

uninstall.sh


View more (4)
4. .env.example 📝 Documentation +13/-0

Add example environment configuration file

• Create new example environment file with representative variable values
• Include all required configuration variables for setup validation
• Provide template for users to understand required environment structure
• Use test values suitable for CI validation and documentation

.env.example


5. .github/workflows/lint.yml ✨ Enhancement +20/-2

Enhance CI validation with compose config checks

• Add apt-get update before package installation to ensure dependency availability
• Fix manage.sh syntax validation to exit with error code on failure instead of silent pass
• Add new docker-compose.yml validation step using representative environment variables
• Export all required environment variables for compose config validation
• Use docker compose config -q for quiet validation of template syntax

.github/workflows/lint.yml


6. README.md 📝 Documentation +12/-23

Update documentation for auto-generated credentials and security

• Update port binding documentation to reflect Plex and Jellyfin now bound to 127.0.0.1 by default
• Simplify Prowlarr setup instructions to reference auto-generated credentials instead of manual
 creation
• Update Radarr and Sonarr setup steps to use auto-generated admin credentials
• Revise security notes to document Forms authentication with Enabled status and auto-generated
 credentials
• Remove reference to DisabledForLocalAddresses auth mode in user-facing documentation
• Clarify that credentials are auto-generated and retrievable via manage.sh keys --show-secrets
• Update architecture notes to reflect localhost-only port binding for all services including media
 servers

README.md


7. docker-compose.yml ✨ Enhancement +3/-3

Bind media server ports to localhost only

• Bind Plex port 32400 to 127.0.0.1 instead of all interfaces
• Bind Jellyfin ports 8096 and 8920 to 127.0.0.1 instead of all interfaces
• Ensure all media server ports are localhost-only by default for security

docker-compose.yml


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Apr 29, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0)

Grey Divider


Action required

1. manage.sh keys command broken 🐞 Bug ≡ Correctness
Description
The generated manage.sh keys command will exit immediately because it uses local in a
top-level case block (not inside a function) while set -euo pipefail is enabled. Additionally,
because the manage.sh content is written via a single-quoted heredoc, the new \$ and \$(...)
escapes are preserved literally, so option parsing and key rendering will not work.
Code

setup.sh[R1199-1236]

+        local show_secrets=false
+        if [[ "\$2" == "--show-secrets" ]]; then
+            show_secrets=true
+        fi
+
+        mask_val() {
+            local val="\$1"
+            if [[ "\$show_secrets" == "true" ]]; then
+                echo "\$val"
+            elif [[ \${#val} -gt 8 ]]; then
+                echo "\${val:0:4}...<hidden>...\${val: -4}"
+            else
+                echo "***<hidden>***"
+            fi
+        }
+
        echo -e "\n${CYAN}━━━━ API Keys ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
-        echo -e "  ${YELLOW}WARNING: Sensitive credentials below. Do not share this output.${NC}\n"
-        echo -e "  ${BOLD}TorBox${NC}    $(env_val TORBOX_API_KEY)"
-        echo -e "  ${BOLD}Radarr${NC}    $(env_val RADARR_API_KEY)"
-        echo -e "  ${BOLD}Sonarr${NC}    $(env_val SONARR_API_KEY)"
-        echo -e "  ${BOLD}Prowlarr${NC}  $(env_val PROWLARR_API_KEY)"
+        if [[ "\$show_secrets" == "true" ]]; then
+            echo -e "  ${YELLOW}WARNING: Sensitive credentials below. Do not share this output.${NC}\n"
+        else
+            echo -e "  ${YELLOW}Secrets masked. Use './manage.sh keys --show-secrets' to reveal.${NC}\n"
+        fi
+
+        echo -e "  ${BOLD}TorBox${NC}    \$(mask_val \"\$(env_val TORBOX_API_KEY)\")"
+        echo -e "  ${BOLD}Radarr${NC}    \$(mask_val \"\$(env_val RADARR_API_KEY)\")"
+        echo -e "  ${BOLD}Sonarr${NC}    \$(mask_val \"\$(env_val SONARR_API_KEY)\")"
+        echo -e "  ${BOLD}Prowlarr${NC}  \$(mask_val \"\$(env_val PROWLARR_API_KEY)\")"
+
        local _radarr_pass _sonarr_pass _prowlarr_pass
-        _radarr_pass="$(env_val RADARR_ADMIN_PASS)"
-        _sonarr_pass="$(env_val SONARR_ADMIN_PASS)"
-        _prowlarr_pass="$(env_val PROWLARR_ADMIN_PASS)"
-        if [[ -n "$_radarr_pass" ]]; then
+        _radarr_pass="\$(env_val RADARR_ADMIN_PASS)"
+        _sonarr_pass="\$(env_val SONARR_ADMIN_PASS)"
+        _prowlarr_pass="\$(env_val PROWLARR_ADMIN_PASS)"
+        if [[ -n "\$_radarr_pass" ]]; then
            echo ""
            echo -e "  ${BOLD}Admin Credentials:${NC}"
-            echo -e "  ${BOLD}Radarr${NC}    user: $(env_val RADARR_ADMIN_USER)  pass: ${_radarr_pass}"
-            echo -e "  ${BOLD}Sonarr${NC}    user: $(env_val SONARR_ADMIN_USER)  pass: ${_sonarr_pass}"
-            echo -e "  ${BOLD}Prowlarr${NC}  user: $(env_val PROWLARR_ADMIN_USER)  pass: ${_prowlarr_pass}"
+            echo -e "  ${BOLD}Radarr${NC}    user: \$(env_val RADARR_ADMIN_USER)  pass: \$(mask_val \"\${_radarr_pass}\")"
+            echo -e "  ${BOLD}Sonarr${NC}    user: \$(env_val SONARR_ADMIN_USER)  pass: \$(mask_val \"\${_sonarr_pass}\")"
+            echo -e "  ${BOLD}Prowlarr${NC}  user: \$(env_val PROWLARR_ADMIN_USER)  pass: \$(mask_val \"\${_prowlarr_pass}\")"
Evidence
manage.sh is generated from a single-quoted heredoc (no expansion), so backslashes added in the
keys) block remain in the generated script, making it treat $2, $1, and $(env_val ...) as
literals. Also, local show_secrets=false is executed at top-level under set -e, which causes an
immediate failure when running ./manage.sh keys.

setup.sh[1034-1038]
setup.sh[1152-1239]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The `keys)` handler written into `manage.sh` is broken:
- `local` is used at top-level (inside a `case` block), causing `./manage.sh keys` to fail under `set -euo pipefail`.
- The handler unnecessarily escapes `$` and command substitutions even though the heredoc is single-quoted (`<< 'MANAGE_EOF'`), so those escapes become literal characters in the generated `manage.sh`, preventing argument parsing and value rendering.

### Issue Context
`manage.sh` is generated by `setup.sh` using single-quoted heredocs, so content is emitted literally; no variable expansion occurs during generation.

### Fix Focus Areas
- setup.sh[1034-1239]

### Implementation notes
In the generated `manage.sh` content:
- Remove `local show_secrets=false` (use `show_secrets=false`) OR move the `keys` logic into a real function (e.g., `keys_cmd() { ... }`) and call it from the `case`.
- Remove the backslashes in `"\$2"`, `"\$1"`, `\${#val}`, `\$(...)`, etc. Use normal bash: `${2:-}`, `$(env_val TORBOX_API_KEY)`, etc.
- Keep `local` only inside actual function bodies (e.g., inside `mask_val()`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Test imports unintended setup chunk 🐞 Bug ☼ Reliability
Description
tests/test_utils.sh sources a sed range for mask_key() that does not stop at the end of the
one-line function definition, so it imports extra unrelated declarations/functions from setup.sh
into the test environment. This makes the tests fragile to unrelated setup.sh edits and can
introduce side effects into test runs.
Code

tests/test_utils.sh[R17-23]

+# Source functions directly from setup.sh to ensure tests match implementation
+SETUP_SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/setup.sh"
+if [[ -f "$SETUP_SCRIPT" ]]; then
+    source <(sed -n '/^generate_api_key() {/,/^}/p' "$SETUP_SCRIPT")
+    source <(sed -n '/^mask_key() /,/^}/p' "$SETUP_SCRIPT" 2>/dev/null || true)
+    # Inline mask_key since it's a one-liner in setup.sh:
+    source <(grep '^mask_key() ' "$SETUP_SCRIPT")
Evidence
In setup.sh, mask_key() is defined on a single line, so the sed range /^mask_key() /,/^}/ does
not terminate on the same line and continues until the next standalone } (currently the end of
print_service_urls()), unintentionally sourcing additional code. Although a grep fallback also
sources mask_key(), the sed-based source still executes and creates unnecessary coupling to
setup.sh layout.

tests/test_utils.sh[17-23]
setup.sh[96-125]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`tests/test_utils.sh` currently does:
- `source <(sed -n '/^mask_key() /,/^}/p' "$SETUP_SCRIPT" ...)`
But `mask_key()` is a one-liner in `setup.sh`, so this sed range captures additional unrelated code until the next line that matches `^}`.

### Issue Context
The test suite intends to import only `generate_api_key()` and `mask_key()` implementations.

### Fix Focus Areas
- tests/test_utils.sh[17-23]
- setup.sh[96-125]

### Suggested fix options
Pick one:
1) **Simplest:** Remove the sed-based `mask_key` sourcing and keep only the `grep '^mask_key() '` one-liner source.
2) **More robust:** Implement a small `extract_func()` helper in tests that extracts either one-line or multi-line function definitions using brace counting (awk), and use it for both functions.
3) **Alternative:** Source `setup.sh` directly in tests (it is guarded by `if [[ "${BASH_SOURCE[0]}" == "${0}" ]]` for execution), and then call the functions from the sourced script (ensure tests don’t depend on setup globals).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: ShellCheck

Failed stage: Lint setup.sh [❌]

Failed test name: ""

Failure summary:

The action failed because shellcheck reported SC2015 findings in setup.sh (e.g., lines 1991, 2002,
2008, 2054) and the workflow treats these findings as a failure (non-zero exit).
Specifically,
SC2015 warns that using A && B || C is not a safe if-then-else pattern because C may run even when A
is true, and this caused the step to exit with code 1 (Process completed with exit code 1).

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

216:  ^-- SC2015 (info): Note that A && B || C is not if-then-else. C may run when A is true.
217:  In setup.sh line 1991:
218:  -o /dev/null 2>/dev/null && log_info "  Plex 'Movies' library added." \
219:  ^-- SC2015 (info): Note that A && B || C is not if-then-else. C may run when A is true.
220:  In setup.sh line 2002:
221:  -o /dev/null 2>/dev/null && log_info "  Plex 'TV Shows' library added." \
222:  ^-- SC2015 (info): Note that A && B || C is not if-then-else. C may run when A is true.
223:  In setup.sh line 2008:
224:  grep -v '^PLEX_CLAIM=' "${ENV_FILE}" > "${ENV_FILE}.tmp" && mv "${ENV_FILE}.tmp" "${ENV_FILE}" || true
225:  ^-- SC2015 (info): Note that A && B || C is not if-then-else. C may run when A is true.
226:  In setup.sh line 2054:
227:  }' -o /dev/null 2>/dev/null && log_info "  Default indexer '1337x' added to Prowlarr." \
228:  ^-- SC2015 (info): Note that A && B || C is not if-then-else. C may run when A is true.
229:  For more information:
230:  https://www.shellcheck.net/wiki/SC2015 -- Note that A && B || C is not if-t...
231:  ##[error]Process completed with exit code 1.
232:  Post job cleanup.

Comment thread setup.sh
Comment on lines +1199 to +1236
local show_secrets=false
if [[ "\$2" == "--show-secrets" ]]; then
show_secrets=true
fi

mask_val() {
local val="\$1"
if [[ "\$show_secrets" == "true" ]]; then
echo "\$val"
elif [[ \${#val} -gt 8 ]]; then
echo "\${val:0:4}...<hidden>...\${val: -4}"
else
echo "***<hidden>***"
fi
}

echo -e "\n${CYAN}━━━━ API Keys ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
echo -e " ${YELLOW}WARNING: Sensitive credentials below. Do not share this output.${NC}\n"
echo -e " ${BOLD}TorBox${NC} $(env_val TORBOX_API_KEY)"
echo -e " ${BOLD}Radarr${NC} $(env_val RADARR_API_KEY)"
echo -e " ${BOLD}Sonarr${NC} $(env_val SONARR_API_KEY)"
echo -e " ${BOLD}Prowlarr${NC} $(env_val PROWLARR_API_KEY)"
if [[ "\$show_secrets" == "true" ]]; then
echo -e " ${YELLOW}WARNING: Sensitive credentials below. Do not share this output.${NC}\n"
else
echo -e " ${YELLOW}Secrets masked. Use './manage.sh keys --show-secrets' to reveal.${NC}\n"
fi

echo -e " ${BOLD}TorBox${NC} \$(mask_val \"\$(env_val TORBOX_API_KEY)\")"
echo -e " ${BOLD}Radarr${NC} \$(mask_val \"\$(env_val RADARR_API_KEY)\")"
echo -e " ${BOLD}Sonarr${NC} \$(mask_val \"\$(env_val SONARR_API_KEY)\")"
echo -e " ${BOLD}Prowlarr${NC} \$(mask_val \"\$(env_val PROWLARR_API_KEY)\")"

local _radarr_pass _sonarr_pass _prowlarr_pass
_radarr_pass="$(env_val RADARR_ADMIN_PASS)"
_sonarr_pass="$(env_val SONARR_ADMIN_PASS)"
_prowlarr_pass="$(env_val PROWLARR_ADMIN_PASS)"
if [[ -n "$_radarr_pass" ]]; then
_radarr_pass="\$(env_val RADARR_ADMIN_PASS)"
_sonarr_pass="\$(env_val SONARR_ADMIN_PASS)"
_prowlarr_pass="\$(env_val PROWLARR_ADMIN_PASS)"
if [[ -n "\$_radarr_pass" ]]; then
echo ""
echo -e " ${BOLD}Admin Credentials:${NC}"
echo -e " ${BOLD}Radarr${NC} user: $(env_val RADARR_ADMIN_USER) pass: ${_radarr_pass}"
echo -e " ${BOLD}Sonarr${NC} user: $(env_val SONARR_ADMIN_USER) pass: ${_sonarr_pass}"
echo -e " ${BOLD}Prowlarr${NC} user: $(env_val PROWLARR_ADMIN_USER) pass: ${_prowlarr_pass}"
echo -e " ${BOLD}Radarr${NC} user: \$(env_val RADARR_ADMIN_USER) pass: \$(mask_val \"\${_radarr_pass}\")"
echo -e " ${BOLD}Sonarr${NC} user: \$(env_val SONARR_ADMIN_USER) pass: \$(mask_val \"\${_sonarr_pass}\")"
echo -e " ${BOLD}Prowlarr${NC} user: \$(env_val PROWLARR_ADMIN_USER) pass: \$(mask_val \"\${_prowlarr_pass}\")"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Manage.sh keys command broken 🐞 Bug ≡ Correctness

The generated manage.sh keys command will exit immediately because it uses local in a
top-level case block (not inside a function) while set -euo pipefail is enabled. Additionally,
because the manage.sh content is written via a single-quoted heredoc, the new \$ and \$(...)
escapes are preserved literally, so option parsing and key rendering will not work.
Agent Prompt
### Issue description
The `keys)` handler written into `manage.sh` is broken:
- `local` is used at top-level (inside a `case` block), causing `./manage.sh keys` to fail under `set -euo pipefail`.
- The handler unnecessarily escapes `$` and command substitutions even though the heredoc is single-quoted (`<< 'MANAGE_EOF'`), so those escapes become literal characters in the generated `manage.sh`, preventing argument parsing and value rendering.

### Issue Context
`manage.sh` is generated by `setup.sh` using single-quoted heredocs, so content is emitted literally; no variable expansion occurs during generation.

### Fix Focus Areas
- setup.sh[1034-1239]

### Implementation notes
In the generated `manage.sh` content:
- Remove `local show_secrets=false` (use `show_secrets=false`) OR move the `keys` logic into a real function (e.g., `keys_cmd() { ... }`) and call it from the `case`.
- Remove the backslashes in `"\$2"`, `"\$1"`, `\${#val}`, `\$(...)`, etc. Use normal bash: `${2:-}`, `$(env_val TORBOX_API_KEY)`, etc.
- Keep `local` only inside actual function bodies (e.g., inside `mask_val()`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment thread setup.sh
show_urls
;;
keys)
local show_secrets=false
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 local used outside a function in generated manage.sh crashes ./manage.sh keys

The keys) case branch at setup.sh:1199 uses local show_secrets=false at the top level of the generated manage.sh script (not inside any function). The case statement starting at setup.sh:1152 is at the script's top level. Since manage.sh starts with set -euo pipefail (setup.sh:1036), bash will print local: can only be used in a function, return exit code 1, and set -e will immediately terminate the script. This completely breaks ./manage.sh keys and ./manage.sh keys --show-secrets. The same issue exists at line 1227 (local _radarr_pass ...), though line 1199 is hit first.

Prompt for agents
In setup.sh, inside the generate_management_script() function, the keys) case branch of the manage.sh heredoc (around line 1199) uses 'local' outside a function. The case statement at line 1152 is at the top level of the generated manage.sh, not inside a function. 'local' can only be used inside functions in bash, and with set -euo pipefail, this causes an immediate script exit.

Fix: Replace 'local show_secrets=false' with just 'show_secrets=false' at line 1199, and replace 'local _radarr_pass _sonarr_pass _prowlarr_pass' with just '_radarr_pass="" _sonarr_pass="" _prowlarr_pass=""' (or simply remove the 'local' keyword) at line 1227. Top-level variables in the case block don't need 'local' scoping.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread setup.sh
Comment on lines +1200 to +1236
if [[ "\$2" == "--show-secrets" ]]; then
show_secrets=true
fi

mask_val() {
local val="\$1"
if [[ "\$show_secrets" == "true" ]]; then
echo "\$val"
elif [[ \${#val} -gt 8 ]]; then
echo "\${val:0:4}...<hidden>...\${val: -4}"
else
echo "***<hidden>***"
fi
}

echo -e "\n${CYAN}━━━━ API Keys ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
echo -e " ${YELLOW}WARNING: Sensitive credentials below. Do not share this output.${NC}\n"
echo -e " ${BOLD}TorBox${NC} $(env_val TORBOX_API_KEY)"
echo -e " ${BOLD}Radarr${NC} $(env_val RADARR_API_KEY)"
echo -e " ${BOLD}Sonarr${NC} $(env_val SONARR_API_KEY)"
echo -e " ${BOLD}Prowlarr${NC} $(env_val PROWLARR_API_KEY)"
if [[ "\$show_secrets" == "true" ]]; then
echo -e " ${YELLOW}WARNING: Sensitive credentials below. Do not share this output.${NC}\n"
else
echo -e " ${YELLOW}Secrets masked. Use './manage.sh keys --show-secrets' to reveal.${NC}\n"
fi

echo -e " ${BOLD}TorBox${NC} \$(mask_val \"\$(env_val TORBOX_API_KEY)\")"
echo -e " ${BOLD}Radarr${NC} \$(mask_val \"\$(env_val RADARR_API_KEY)\")"
echo -e " ${BOLD}Sonarr${NC} \$(mask_val \"\$(env_val SONARR_API_KEY)\")"
echo -e " ${BOLD}Prowlarr${NC} \$(mask_val \"\$(env_val PROWLARR_API_KEY)\")"

local _radarr_pass _sonarr_pass _prowlarr_pass
_radarr_pass="$(env_val RADARR_ADMIN_PASS)"
_sonarr_pass="$(env_val SONARR_ADMIN_PASS)"
_prowlarr_pass="$(env_val PROWLARR_ADMIN_PASS)"
if [[ -n "$_radarr_pass" ]]; then
_radarr_pass="\$(env_val RADARR_ADMIN_PASS)"
_sonarr_pass="\$(env_val SONARR_ADMIN_PASS)"
_prowlarr_pass="\$(env_val PROWLARR_ADMIN_PASS)"
if [[ -n "\$_radarr_pass" ]]; then
echo ""
echo -e " ${BOLD}Admin Credentials:${NC}"
echo -e " ${BOLD}Radarr${NC} user: $(env_val RADARR_ADMIN_USER) pass: ${_radarr_pass}"
echo -e " ${BOLD}Sonarr${NC} user: $(env_val SONARR_ADMIN_USER) pass: ${_sonarr_pass}"
echo -e " ${BOLD}Prowlarr${NC} user: $(env_val PROWLARR_ADMIN_USER) pass: ${_prowlarr_pass}"
echo -e " ${BOLD}Radarr${NC} user: \$(env_val RADARR_ADMIN_USER) pass: \$(mask_val \"\${_radarr_pass}\")"
echo -e " ${BOLD}Sonarr${NC} user: \$(env_val SONARR_ADMIN_USER) pass: \$(mask_val \"\${_sonarr_pass}\")"
echo -e " ${BOLD}Prowlarr${NC} user: \$(env_val PROWLARR_ADMIN_USER) pass: \$(mask_val \"\${_prowlarr_pass}\")"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Incorrect \$ escaping in single-quoted heredoc breaks all command substitutions in keys command

Lines 1200-1236 use \$ for variable references and command substitutions (e.g., \$2, \$(env_val ...), \$(mask_val ...)). However, this code is inside a single-quoted heredoc (<< 'MANAGE_EOF' at setup.sh:1096), which means no expansion or escape processing occurs — content is written literally to manage.sh. The \$ sequences appear as-is in the generated file. When bash later executes manage.sh, "\$2" evaluates to the literal string $2 (not the second positional argument), and "\$(env_val ...)" evaluates to the literal string $(env_val ...) (not a command substitution).

Comparison with old working code

The old code correctly used unescaped $ in the same single-quoted heredoc:

# OLD (correct): command substitution executes at runtime
echo -e "  ${BOLD}TorBox${NC}    $(env_val TORBOX_API_KEY)"

# NEW (broken): literal text output
echo -e "  ${BOLD}TorBox${NC}    \$(mask_val \"\$(env_val TORBOX_API_KEY)\")"

The fix is to remove all \ before $ and " in the keys section, since single-quoted heredocs don't need escaping.

Prompt for agents
In setup.sh, the keys) case branch (lines 1198-1239) is inside a single-quoted heredoc (<< 'MANAGE_EOF' at line 1096). In single-quoted heredocs, NO expansion or escape processing occurs — everything is written literally to the output file. The PR incorrectly added backslash escaping (\$, \") as if the heredoc were unquoted.

Fix: Remove all backslash escaping of $ and " throughout lines 1200-1236. For example:
- Change '\$2' to '$2'
- Change '\$(env_val ...)' to '$(env_val ...)'
- Change '\$(mask_val ...)' to '$(mask_val ...)'
- Change '\"' to '"'
- Change '\${#val}' to '${#val}'
- Change '\${val:0:4}' to '${val:0:4}'

The old code on the same heredoc correctly used $(env_val ...) without escaping. The mask_val function definition (lines 1204-1213) also needs the same fix for its internal variable references.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@nordicnode nordicnode merged commit d3f22e6 into main Apr 29, 2026
2 of 3 checks passed
@nordicnode nordicnode deleted the fix-all-reported-issues-7776805136999684067 branch April 29, 2026 14:42
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.

1 participant