Skip to content

Conversation

@jokob-sk
Copy link
Collaborator

@jokob-sk jokob-sk commented Feb 8, 2026

In scope:

  • Redirect support with deep links and anchors
  • Previous functionality should be preserved
  • Most critical: No password/login bypass issues

Out of scope:

Adding additional security checks

Summary by CodeRabbit

  • New Features

    • Centralized session-based authentication with explicit login/logout and session regeneration
    • Persistent "Remember Me" that securely restores sessions via cookie tokens and respects secure cookie attributes
    • Login form enhancements: preserves intended destination and URL hash across redirects; client script captures hash
  • Bug Fixes

    • Fixed redirect/auth flow to reliably preserve and restore the user's destination and state after login
    • Auto-login behavior adjusted when protection is disabled

jokob-sk and others added 3 commits February 9, 2026 09:38
Signed-off-by: jokob-sk <jokob.sk@gmail.com>
Signed-off-by: jokob-sk <jokob.sk@gmail.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 8, 2026

📝 Walkthrough

Walkthrough

Replaces ad‑hoc auth checks with a centralized session-based authentication flow in the front-end, adding login/logout helpers, Remember Me token generation/validation, secure cookie handling, redirect/next handling (preserving URL hash), and client-side scripts to capture URL hash and manage login UI state.

Changes

Cohort / File(s) Summary
Auth controller & UI
front/index.php
Introduces session-centric auth: COOKIE_NAME, DEFAULT_REDIRECT, helpers (safe_redirect, validate_local_path, append_hash, is_authenticated, login_user, logout_user, is_https_request), login/logout flows, Remember Me token creation/validation, secure cookie attributes, redirect handling preserving URL hash, and client-side script integration for next and url_hash.
Startup / redirect tweaks
front/php/templates/security.php
Ensures session started at startup and updates redirect logic to encode current REQUEST_URI as base64+urlencoded next parameter when forcing login. Removed redundant session_start blocks.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Browser
    participant Server as front/index.php
    participant Session as SessionStore
    participant DB as TokenStore

    Browser->>Server: GET /index.php (with cookie? url_hash)
    Server->>Session: session_start / read auth state
    alt RememberMe cookie present
        Server->>DB: lookup hashed token
        DB-->>Server: token matches?
        alt valid
            Server->>Session: set authenticated user, regenerate session
            Server-->>Browser: Set session cookie, optional renewed RememberMe cookie, Redirect to next + hash
        else invalid
            Server-->>Browser: Redirect to /index.php?next=... (login page)
        end
    else no cookie / not authenticated
        Server-->>Browser: Redirect to /index.php?next=... (login page)
    end

    Browser->>Server: POST /index.php (login form, password, remember flag, url_hash)
    Server->>DB: verify password hash (and store generated remember token if requested)
    Server->>Session: login_user(), regenerate session, store token hash
    Server-->>Browser: Set HttpOnly SameSite=... Secure cookie for RememberMe (if chosen), Redirect to validated next + preserved url_hash
Loading

Poem

I'm a rabbit in code, nibbling keys tonight,
I stitched a session blanket, snug and tight.
Cookies tucked in, tokens safe and warm,
Hashes hop home, through server storm.
Hop — click, redirect — a crescent moon's light 🐇✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main changes: deep link support after login and refactoring of index.php. It is specific, concise, and directly related to the primary objectives of the changeset.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch next_release

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
front/index.php (1)

122-146: SHA-256 is a weak choice for password hashing — consider migrating to password_hash/password_verify.

SHA-256 is a fast hash not designed for passwords; it's susceptible to brute-force and GPU-accelerated attacks. PHP's password_hash() with PASSWORD_DEFAULT (bcrypt/argon2) is the idiomatic and secure approach.

This appears to be a pre-existing pattern ($nax_Password is already stored as SHA-256), so it's likely out of scope for this PR, but worth tracking for a future improvement.

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (1)
front/php/templates/security.php (1)

85-85: ⚠️ Potential issue | 🔴 Critical

Cookie validation is now incompatible with the new token-based cookie set in index.php.

index.php now stores a random token in the COOKIE_NAME cookie (line 108 of index.php), but this guard still compares the cookie value against $nax_Password (the config password hash). After this PR the cookie will never contain the password hash, so this fallback path is dead code — users with an expired session but a valid remember-me cookie will be redirected to login instead of passing through.

Either remove this cookie check (let index.php handle remember-me on redirect) or align it with the new token-based scheme.

🤖 Fix all issues with AI agents
In `@front/index.php`:
- Around line 125-131: The remember-me flow is broken because the server-side
token is only stored in $_SESSION['remember_token'] which is lost when sessions
expire; persist the server-side token in a durable store (database or file) and
update the login and check logic to use it: when creating a remember-me cookie
(in your login path where login_user() is called) generate and store the token
hash in the persistent store tied to the user and set the cookie; in the check
block (where you currently read $_COOKIE[COOKIE_NAME] and compare to
$_SESSION['remember_token']) query the persistent store for the user's stored
token (e.g., via a new function like get_persistent_remember_token($userId) or
lookup_remember_token_by_cookie()) and use hash_equals(hash('sha256',
$_COOKIE[COOKIE_NAME]), $storedHash) to authenticate, then call login_user(),
repopulate $_SESSION['remember_token'], and
safe_redirect(append_hash($redirectTo)); ensure token creation, lookup,
invalidation on logout, and rotation are implemented.
- Around line 9-11: security.php (required in front/index.php) already calls
session_start(), so the extra session_start() in front/index.php causes a
duplicate session start warning; fix by removing the redundant session_start()
call in front/index.php or guard it with a check like session_status() !==
PHP_SESSION_ACTIVE before calling session_start(), referencing the existing
session_start() in security.php to avoid double invocation.
- Around line 25-37: The validate_local_path function currently rejects query
strings because the regex ^/[a-zA-Z0-9_\-/\.]*$ excludes characters like ?, =,
&, and :, so deep links (e.g., /devices.php?mac=AA:BB:CC) get replaced with
DEFAULT_REDIRECT; update the regex in validate_local_path to allow safe query
characters (e.g., ?, =, &, :, %, #) while still enforcing it starts with '/' and
forbidding whitespace/newlines and protocol prefixes like '//' or 'http(s)://';
for example, add a negative lookahead to reject '//' or '://', and expand the
character class to include \?\=\&\:\%\# so the function returns decoded
deep-links instead of DEFAULT_REDIRECT.
- Around line 77-80: The current block unconditionally calls login_user() (which
calls session_regenerate_id(true)) when $nax_WebProtection !== 'true', causing a
session ID regen on every index.php load; change the logic to skip calling
login_user() if the request is already authenticated (e.g., detect existing
session/auth state via your app's auth check or session flag) and only call
login_user() when the user is not authenticated, then proceed with
safe_redirect(append_hash($redirectTo)) as before; reference login_user(),
session_regenerate_id(true), safe_redirect(), and append_hash() to locate and
update the code.
🧹 Nitpick comments (1)
front/index.php (1)

13-14: Duplicate cookie-name constant.

security.php already defines COOKIE_SAVE_LOGIN_NAME = "NetAlertX_SaveLogin" (line 16). Here COOKIE_NAME is defined with the same value. Use a single shared constant to avoid drift.

Comment on lines 9 to 11
require_once $_SERVER['DOCUMENT_ROOT'].'/php/templates/security.php';

$CookieSaveLoginName = 'NetAlertX_SaveLogin';
session_start();
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Double session_start() will trigger a PHP warning.

security.php (required on line 9) already calls session_start() at its line 49. Calling it again here on line 11 produces a PHP warning in 7.2+. Guard it or remove the duplicate.

Proposed fix
-session_start();
+if (session_status() === PHP_SESSION_NONE) {
+    session_start();
+}
🤖 Prompt for AI Agents
In `@front/index.php` around lines 9 - 11, security.php (required in
front/index.php) already calls session_start(), so the extra session_start() in
front/index.php causes a duplicate session start warning; fix by removing the
redundant session_start() call in front/index.php or guard it with a check like
session_status() !== PHP_SESSION_ACTIVE before calling session_start(),
referencing the existing session_start() in security.php to avoid double
invocation.

Signed-off-by: jokob-sk <jokob.sk@gmail.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@front/index.php`:
- Around line 107-113: The secure flag in the setcookie call for COOKIE_NAME is
being set using isset($_SERVER['HTTPS']), which is unreliable (IIS can set
'off'); update the secure check used when calling setcookie(COOKIE_NAME, $token,
...) to a robust is-HTTPS test such as checking that $_SERVER['HTTPS'] is
non-empty and not equal to 'off' and/or honoring proxy headers like
HTTP_X_FORWARDED_PROTO == 'https' when behind a trusted proxy; replace the
current isset($_SERVER['HTTPS']) expression with that combined HTTPS detection
logic so the 'secure' option is only true for actual HTTPS requests.
- Around line 54-64: The logout_user function clears the remember-me cookie but
omits the secure flag, so the deletion cookie may not match the original (set
with 'secure' => isset(...)); update logout_user to include the same secure flag
logic used when creating the cookie (use the same condition used during login)
when calling setcookie for COOKIE_NAME so the deletion cookie's attributes
(secure, samesite, path) match the original and ensure the browser will remove
the cookie.
- Line 193: The form action embeds the base64 `$_GET['next']` but a `+` in
base64 can be interpreted as space when round-tripped via query parsing; to fix,
URL-encode the `$_GET['next']` value before rendering it into the action
attribute (e.g. wrap the value with rawurlencode or urlencode) while still
applying htmlspecialchars for HTML context so the output remains safe; update
the code in front/index.php where `$_GET['next']` is printed (the form action
line) and ensure this is consistent with the `validate_local_path` handling of
the base64 next token.
🧹 Nitpick comments (1)
front/index.php (1)

94-118: No feedback to the user on a failed login attempt.

When the password check fails (line 98 evaluates to false), execution silently falls through to displaying the login form again with no error message. Users won't know whether they mistyped or something else went wrong.

Consider setting a flag (e.g., $login_error = true) and displaying a message in the form.

front/index.php Outdated
<div class="login-box-body">
<p class="login-box-msg"><?= lang('Login_Box');?></p>
<form action="index.php" method="post">
<form action="index.php<?php echo !empty($_GET['next']) ? '?next=' . htmlspecialchars($_GET['next']) : ''; ?>" method="post">
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 8, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Form action preserves next param correctly, but consider encoding consistency.

$_GET['next'] is already base64-encoded per validate_local_path, and htmlspecialchars is applied here — this looks correct. However, note that the + character in base64 output will survive htmlspecialchars but could be interpreted as a space by the receiving $_GET. Consider using urlencode() around the value for robustness.

Proposed fix
-      <form action="index.php<?php echo !empty($_GET['next']) ? '?next=' . htmlspecialchars($_GET['next']) : ''; ?>" method="post">
+      <form action="index.php<?php echo !empty($_GET['next']) ? '?next=' . htmlspecialchars(urlencode($_GET['next'])) : ''; ?>" method="post">
🤖 Prompt for AI Agents
In `@front/index.php` at line 193, The form action embeds the base64
`$_GET['next']` but a `+` in base64 can be interpreted as space when
round-tripped via query parsing; to fix, URL-encode the `$_GET['next']` value
before rendering it into the action attribute (e.g. wrap the value with
rawurlencode or urlencode) while still applying htmlspecialchars for HTML
context so the output remains safe; update the code in front/index.php where
`$_GET['next']` is printed (the form action line) and ensure this is consistent
with the `validate_local_path` handling of the base64 next token.

Copy link
Member

Choose a reason for hiding this comment

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

This is outdated and can be closed. Coderabbit can raise a new issue.

It's sucgesting to urlencode first before HTML special chars

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

@adamoutler, understood! I'll create a new issue to track the URL encoding concern properly.


🏁 Script executed:

#!/bin/bash
# Create a GitHub issue for the URL encoding concern
gh issue create \
  --title "URL encode base64 next parameter before htmlspecialchars in login form" \
  --body "## Description

When embedding the base64-encoded \`\$_GET['next']\` parameter in the login form action attribute in \`front/index.php\`, the \`+\` character from base64 encoding can be misinterpreted as a space during URL parsing.

## Suggested Fix

Apply \`urlencode()\` before \`htmlspecialchars()\` when building the form action:

\`\`\`php
?next=' . htmlspecialchars(urlencode(\$_GET['next']), ENT_QUOTES, 'UTF-8')
\`\`\`

This ensures the base64 value survives the round-trip through query string parsing.

## Context

- **File:** \`front/index.php\` (form action around line 193)
- **Original PR:** `#1500`
- **Comment thread:** https://github.com/netalertx/NetAlertX/pull/1500#discussion_r2780043435
- **Requested by:** `@adamoutler`" \
  --assignee adamoutler

Length of output: 50


@adamoutler, done! I've created issue #1501 to track the URL encoding concern: #1501

The issue documents the suggestion to apply urlencode() before htmlspecialchars() for the next parameter to handle base64 + characters correctly.


🧬 Code Graph Analysis Results

front/php/templates/language/lang.php

### Snippet from `front/php/templates/language/lang.php` (lines 90-118)

function lang($key)
{
global $pia_lang_selected, $strings;

// Get the data from JSON files
$languageData = getLanguageDataFromJson();

// Get the data from SQL query
$sqlLanguageData = $strings;

// Merge JSON data with SQL data
$mergedLanguageData = mergeLanguageData($languageData, $sqlLanguageData);

// Check if the key exists in the selected language
if (isset($mergedLanguageData[$pia_lang_selected][$key]) && $mergedLanguageData[$pia_lang_selected][$key] != '') {
    $result = $mergedLanguageData[$pia_lang_selected][$key];
} else {
    // If key not found in selected language, use "en_us" as fallback
    if (isset($mergedLanguageData['en_us'][$key])) {
        $result = $mergedLanguageData['en_us'][$key];
    } else {
        // If key not found in "en_us" either, use a default string
        $result = "String Not found for key " . $key;
    }
}

// HTML encode the result before returning
return str_replace("'", '&#39;', $result);

}

Copy link
Member

@adamoutler adamoutler Feb 9, 2026

Choose a reason for hiding this comment

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

I didn't mean that. I mean usually when you put a comment in a code review, it's an issue in the code. We don't need a new project issue from code in a PR. Please close. I didn't know you had that capability.

Copy link
Member

Choose a reason for hiding this comment

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

I thought you'd raise a non-outdated review comment or renew it or something.

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Close the issue `#1501` that was created
gh issue close 1501 --comment "Closing as requested - this was created from a PR code review comment by mistake."

Length of output: 60


🏁 Script executed:

#!/bin/bash
# Close the issue `#1501` without adding a comment
gh issue close 1501

Length of output: 60


@adamoutler, I apologize for the confusion! You're absolutely right—I misunderstood your comment. Code review comments are meant to flag issues in the code during review, not spawn separate GitHub issues.

Unfortunately, I don't have the necessary permissions to close issue #1501. You'll need to close it manually, or I can ask a maintainer to do so if you prefer.

I've noted this for future interactions to avoid creating issues from PR code review comments unless you explicitly request it.

  (\__/)
  (•ㅅ•)  *sorry!*
 _ノヽ ノ\_

🧬 Code Graph Analysis Results

: front/php/templates/language/lang.php

At lines 90-118:

function lang($key)
{
    global $pia_lang_selected, $strings;

    // Get the data from JSON files
    $languageData = getLanguageDataFromJson();

    // Get the data from SQL query
    $sqlLanguageData = $strings;

    // Merge JSON data with SQL data
    $mergedLanguageData = mergeLanguageData($languageData, $sqlLanguageData);

    // Check if the key exists in the selected language
    if (isset($mergedLanguageData[$pia_lang_selected][$key]) && $mergedLanguageData[$pia_lang_selected][$key] != '') {
        $result = $mergedLanguageData[$pia_lang_selected][$key];
    } else {
        // If key not found in selected language, use "en_us" as fallback
        if (isset($mergedLanguageData['en_us'][$key])) {
            $result = $mergedLanguageData['en_us'][$key];
        } else {
            // If key not found in "en_us" either, use a default string
            $result = "String Not found for key " . $key;
        }
    }

    // HTML encode the result before returning
    return str_replace("'", '&#39;', $result);
}

✏️ Learnings added
Learnt from: adamoutler
Repo: netalertx/NetAlertX PR: 1500
File: front/index.php:0-0
Timestamp: 2026-02-09T03:43:16.357Z
Learning: Do not create GitHub issues from PR code review comments unless the user explicitly requests issue creation. Code review comments are meant to flag issues in the code during review, not spawn separate GitHub issues.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Member

Choose a reason for hiding this comment

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

Please add to learnings: AdamOutler will never ask you to open a new issue on GitHub. If he did, you misunderstood

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

@adamoutler, got it—my apologies for the confusion! Let me close that issue and take a fresh look at the current code.


🏁 Script executed:

#!/bin/bash
# Close the issue I created
gh issue close 1501 --comment "Closing per maintainer request - this was created in error. The concern should be addressed as a review comment on the PR itself, not as a separate issue."

Length of output: 60


🏁 Script executed:

#!/bin/bash
# Check how the 'next' parameter is being created in security.php
cat front/php/templates/security.php

Length of output: 3357


@adamoutler, I understand now—apologies for the confusion! Unfortunately, I don't have permission to close issue #1501 (got a "Resource not accessible by integration" error), so you'll need to close it manually.

Looking at the current code:

In security.php (line 93):

$returnUrl = rawurlencode(base64_encode($_SERVER['REQUEST_URI']));
redirect("/index.php?next=" . $returnUrl);

In index.php (line 193):

?next=' . htmlspecialchars($_GET['next'])

The flow is: security.php URL-encodes the base64 value when creating the redirect, PHP auto-decodes it into $_GET['next'], then index.php embeds it in the form action with only htmlspecialchars(). Technically, base64 + characters in $_GET['next'] would need urlencode() when re-embedded in a query string to survive the round-trip properly.

However, since you marked the original comment as "outdated," I'm assuming either:

  • This is a known non-issue in practice, or
  • You've addressed it differently, or
  • You disagree with the concern

Let me know if you'd like me to verify anything else or if this can be considered resolved!


🧬 Code Graph Analysis Results front/php/templates/language/lang.php ### Snippet from `front/php/templates/language/lang.php` (lines 90-118)
function lang($key)
{
    global $pia_lang_selected, $strings;

    // Get the data from JSON files
    $languageData = getLanguageDataFromJson();

    // Get the data from SQL query
    $sqlLanguageData = $strings;

    // Merge JSON data with SQL data
    $mergedLanguageData = mergeLanguageData($languageData, $sqlLanguageData);

    // Check if the key exists in the selected language
    if (isset($mergedLanguageData[$pia_lang_selected][$key]) && $mergedLanguageData[$pia_lang_selected][$key] != '') {
        $result = $mergedLanguageData[$pia_lang_selected][$key];
    } else {
        // If key not found in selected language, use "en_us" as fallback
        if (isset($mergedLanguageData['en_us'][$key])) {
            $result = $mergedLanguageData['en_us'][$key];
        } else {
            // If key not found in "en_us" either, use a default string
            $result = "String Not found for key " . $key;
        }
    }

    // HTML encode the result before returning
    return str_replace("'", '&#39;', $result);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

@adamoutler, got it! I've added that to my knowledge base. I won't create GitHub issues based on your comments—if I think you're asking for one, I'll know I've misunderstood. 🐰


🧠 Learnings used
Learnt from: adamoutler
Repo: netalertx/NetAlertX PR: 1500
File: front/index.php:0-0
Timestamp: 2026-02-09T03:43:16.357Z
Learning: Do not create GitHub issues from PR code review comments unless the user explicitly requests issue creation. Code review comments are meant to flag issues in the code during review, not spawn separate GitHub issues.

Signed-off-by: jokob-sk <jokob.sk@gmail.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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)
front/php/templates/security.php (2)

54-59: ⚠️ Potential issue | 🟠 Major

This logout handler pre-empts index.php's logout_user(), bypassing its improved cookie cleanup.

As noted in the index.php review, this block runs before index.php line 94 is reached. The setcookie call here (line 57) uses minimal attributes (no httponly, no samesite, no path), while logout_user() in index.php sets all of them. Either consolidate logout into one location or forward to index.php's handler.

Proposed fix — let index.php own logout
 // Handle logout
-if (!empty($_REQUEST['action']) && $_REQUEST['action'] == 'logout') {
-    session_destroy();
-    setcookie(COOKIE_SAVE_LOGIN_NAME, "", time() - 3600);
-    redirect('index.php');
-}
+// Logout is handled by index.php — removed to avoid duplicate/inconsistent cleanup

86-86: ⚠️ Potential issue | 🟠 Major

Pre-existing: raw password hash in cookie enables trivial auth bypass.

The condition $_COOKIE[COOKIE_SAVE_LOGIN_NAME] && $nax_Password === $_COOKIE[COOKIE_SAVE_LOGIN_NAME] compares the stored password hash directly against the cookie value. Anyone who obtains the SHA-256 hash (e.g., from a config file leak) can set this cookie and bypass authentication entirely. This predates this PR but is worth flagging given the PR's stated goal of "no password/login bypass issues."

The new remember-me flow in index.php uses a proper random token with hash_equals, which is the right pattern — but this old path remains active and undermines it.

🤖 Fix all issues with AI agents
In `@front/index.php`:
- Around line 45-50: The append_hash function accepts raw $_POST['url_hash'] and
can produce a malformed URL if the value doesn't start with '#'; update
append_hash to first check $_POST['url_hash'], normalize it so it begins with a
single '#' (prepend one if missing), then sanitize the rest using the existing
character whitelist (preg_replace('/[^#a-zA-Z0-9_\-]/', '', ...)) or better
apply the regex to the substring after the '#' to avoid stripping/duplicating
the prefix; ensure you return $url . $normalizedHash and reference append_hash
and $_POST['url_hash'] when making the change.
- Around line 94-97: The logout code in security.php currently intercepts
?action=logout and uses a simple setcookie/redirect which bypasses the secure
cookie handling in logout_user() defined in index.php; fix by replacing the
logout branch in security.php (the check of $_REQUEST['action'] == 'logout' that
calls session_destroy() and setcookie(COOKIE_SAVE_LOGIN_NAME, "", ...),
redirect('index.php')) with a call to logout_user() (or move logout_user() into
a shared include and call it) and then redirect('index.php')/exit so the same
httponly and samesite='Strict' cookie clearing logic is used; alternatively
remove the unreachable logout block in index.php if you prefer centralizing in
security.php — but ensure the final handler uses logout_user() semantics for
secure cookie attributes.
🧹 Nitpick comments (1)
front/index.php (1)

23-26: safe_redirect injects $path directly into the Location header.

While validate_local_path constrains the character set, safe_redirect itself performs no validation. If it's ever called with an unvalidated argument (now or in the future), it becomes an open-redirect / header-injection vector. A defensive check here would harden the function.

Proposed hardening
 function safe_redirect(string $path): void {
+    // Defense-in-depth: strip newlines to prevent header injection
+    $path = str_replace(["\r", "\n"], '', $path);
     header("Location: {$path}", true, 302);
     exit;
 }

Comment on lines 45 to 50
function append_hash(string $url): string {
if (!empty($_POST['url_hash'])) {
return $url . preg_replace('/[^#a-zA-Z0-9_\-]/', '', $_POST['url_hash']);
}
return $url;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

append_hash doesn't validate that the value starts with #, producing a malformed URL if manipulated.

The JS (line 257-259) always provides window.location.hash (which starts with #), but a crafted POST could send url_hash=abc — the regex on line 47 preserves alphanumerics, resulting in a redirect to e.g. /devices.phpabc. Consider enforcing the # prefix:

Proposed fix
 function append_hash(string $url): string {
     if (!empty($_POST['url_hash'])) {
-        return $url . preg_replace('/[^#a-zA-Z0-9_\-]/', '', $_POST['url_hash']);
+        $sanitized = preg_replace('/[^#a-zA-Z0-9_\-]/', '', $_POST['url_hash']);
+        if (str_starts_with($sanitized, '#')) {
+            return $url . $sanitized;
+        }
     }
     return $url;
 }
🤖 Prompt for AI Agents
In `@front/index.php` around lines 45 - 50, The append_hash function accepts raw
$_POST['url_hash'] and can produce a malformed URL if the value doesn't start
with '#'; update append_hash to first check $_POST['url_hash'], normalize it so
it begins with a single '#' (prepend one if missing), then sanitize the rest
using the existing character whitelist (preg_replace('/[^#a-zA-Z0-9_\-]/', '',
...)) or better apply the regex to the substring after the '#' to avoid
stripping/duplicating the prefix; ensure you return $url . $normalizedHash and
reference append_hash and $_POST['url_hash'] when making the change.

@jokob-sk
Copy link
Collaborator Author

jokob-sk commented Feb 9, 2026

Would appreciate you having a look @adamoutler once you have time - what are valid issues vs white noise - don't want to introduce security issues

@adamoutler
Copy link
Member

Generally when I call BS on coderabbit it's because I know the code. I'm able to say no we can't do that because... Or this isn't important to me because ixxx out of scope.. or xxx was a compromise for yyy... Or this is not code I feel comfortable working with.

These appear valid issues. Some of them may be legacy code. I'd accept all of code rabbit's committable suggestions, and then run unit tests and close out those issues. Then work through the rest one by one.

@jokob-sk
Copy link
Collaborator Author

@coderabbitai - re-review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 10, 2026

@jokob-sk, I'll review the changes in this PR now, with particular attention to the authentication and security aspects you and @adamoutler discussed.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@jokob-sk
Copy link
Collaborator Author

It looks good to me, but if one of you could have a look it would be greatly appreciated @Sakuya9461 @nicocha30 - as you reported security issues in the past - thanks a lot in advance.

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