mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-03-07 16:47:03 -05:00
Complete Content-Type application/json fix for all AJAX responses - Add missing return statements to all ->response->setJSON() calls - Fix Items.php method calls from JSON() to setJSON() - Convert echo statements to proper JSON responses - Ensure consistent Content-Type headers across all controllers - Fix 46+ instances across 12 controller files - Change Config.php methods to : ResponseInterface (all return setJSON only): - postSaveRewards(), postSaveBarcode(), postSaveReceipt() - postSaveInvoice(), postRemoveLogo() - Update PHPDoc @return tags - Change Receivings.php _reload() to : string (only returns view) - Change Receivings.php methods to : string (all return _reload()): - getIndex(), postSelectSupplier(), postChangeMode(), postAdd() - postEditItem(), getDeleteItem(), getRemoveSupplier() - postComplete(), postRequisitionComplete(), getReceipt(), postCancelReceiving() - Change postSave() to : ResponseInterface (returns setJSON) - Update all PHPDoc @return tags Fix XSS vulnerabilities in sales templates, login, and config pages This commit addresses 5 XSS vulnerabilities by adding proper escaping to all user-controlled configuration values in HTML contexts. Fixed Files: - app/Views/sales/invoice.php: Escaped company_logo (URL context) and company (HTML) - app/Views/sales/work_order.php: Escaped company_logo (URL context) - app/Views/sales/receipt_email.php: Added file path validation and escaping for logo - app/Views/login.php: Escaped all config values in title, logo src, and alt - app/Views/configs/info_config.php: Escaped company_logo (URL context) Security Impact: - Prevents stored XSS attacks if configuration is compromised - Defense-in-depth principle applied to administrative interfaces - Follows OWASP best practices for output encoding Testing: - Verified no script execution with XSS payloads in config values - Confirmed proper escaping in HTML, URL, and file contexts - All templates render correctly with valid configuration Severity: High (4 files), Medium-High (1 file) CVSS Score: ~6.1 CWE: CWE-79 (Improper Neutralization of Input During Web Page Generation) Fix critical password validation bypass and add unit tests This commit addresses a critical security vulnerability where the password minimum length check was performed on the HASHED password (always 60 characters for bcrypt) instead of the actual password before hashing. Vulnerability Details: - Original code: strlen($employee_data['password']) >= 8 - This compared the hash length (always 60) instead of raw password - Impact: Users could set 1-character passwords like "a" - Severity: Critical (enables brute force attacks on weak passwords) - CVE-like issue: CWE-307 (Improper Restriction of Excessive Authentication Attempts) Fix Applied: - Validate password length BEFORE hashing - Clear error message when password is too short - Added unit tests to verify minimum length enforcement - Regression test to prevent future vulnerability re-introduction Test Coverage: - testPasswordMinLength_Rejects7Characters: Verify 7 chars rejected - testPasswordMinLength_Accepts8Characters: Verify 8 chars accepted - testPasswordMinLength_RejectsEmptyString: Verify empty rejected - testPasswordMinLength_RejectsWhitespaceOnly: Verify whitespace rejected - testPasswordMinLength_AcceptsSpecialCharacters: Verify special chars OK - testPasswordMinLength_RejectsPreviousBehavior: Regression test for bug Files Modified: - app/Controllers/Home.php: Fixed password validation logic - tests/Controllers/HomeTest.php: Added comprehensive unit tests Security Impact: - Enforces 8-character minimum password policy - Prevents extremely weak passwords that facilitate brute-force attacks - Critical for credential security and user account protection Breaking Changes: - Users with passwords < 8 characters will need to reset their password - This is the intended security improvement Severity: Critical CVSS Score: ~7.5 CWE: CWE-305 (Authentication Bypass by Primary Weakness), CWE-307 Add GitHub Actions workflow to run PHPUnit tests Move business logic from views to controllers for better separation of concerns - Move logo URL computation from info_config view to Config::getIndex() - Move image base64 encoding from receipt_email view to Sales controller - Improves separation of concerns by keeping business logic in controllers - Simplifies view templates to only handle presentation Fix XSS vulnerabilities in report views - escape user-controllable summary data and labels Fix base64 encoding URL issue in delete payment - properly URL encode base64 string Fix remaining return type declarations for Sales controller Fixed additional methods that call _reload(): - postAdd() - returns _reload($data) - postAddPayment() - returns _reload($data) - postEditItem() - returns _reload($data) - postSuspend() - returns _reload($data) - postSetPaymentType() - returns _reload() All methods now return ResponseInterface|string to match _reload() signature. This resolves PHP TypeError errors.
121 lines
8.4 KiB
PHP
121 lines
8.4 KiB
PHP
<?php
|
|
/**
|
|
* @var bool $has_errors
|
|
* @var bool $is_latest
|
|
* @var string $latest_version
|
|
* @var bool $gcaptcha_enabled
|
|
* @var array $config
|
|
* @var $validation
|
|
*/
|
|
?>
|
|
|
|
<!doctype html>
|
|
<html lang="<?= current_language_code() ?>">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<base href="<?= base_url() ?>">
|
|
<title><?= esc($config['company']) . ' | ' . esc(lang('Common.software_short')) . ' | ' . esc(lang('Login.login')) ?></title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="robots" content="noindex, nofollow">
|
|
<link rel="shortcut icon" type="image/x-icon" href="images/favicon.ico">
|
|
<?php
|
|
$theme = (empty($config['theme'])
|
|
|| 'paper' == $config['theme']
|
|
|| 'readable' == $config['theme']
|
|
? 'flatly'
|
|
: $config['theme']);
|
|
?>
|
|
<link rel="stylesheet" href="resources/bootswatch5/<?= "$theme" ?>/bootstrap.min.css">
|
|
<link rel="stylesheet" href="css/login.css">
|
|
<meta name="theme-color" content="#2c3e50">
|
|
</head>
|
|
|
|
<body class="bg-secondary-subtle d-flex flex-column">
|
|
<main class="d-flex justify-content-around align-items-center flex-grow-1">
|
|
<div class="container-login container-fluid d-flex flex-column flex-md-row bg-body shadow rounded m-3 p-4 p-md-0">
|
|
<div class="box-logo d-flex flex-column justify-content-center align-items-center border-end border-secondary-subtle px-4 pb-3 p-md-4">
|
|
<?php if (isset($config['company_logo']) && !empty($config['company_logo'])): ?>
|
|
<img class="logo w-100" src="<?= base_url('uploads/' . esc($config['company_logo'], 'url')) ?>" alt="<?= esc(lang('Common.logo') . ' ' . $config['company']) ?>">
|
|
<?php else: ?>
|
|
<svg class="logo text-primary" role="img" viewBox="0 0 308.57998 308.57997" xmlns="http://www.w3.org/2000/svg">
|
|
<title><?= lang('Common.software_title') . ' ' . lang('Common.logo') ?></title>
|
|
<circle cx="154.28999" cy="154.28999" r="154.28999" fill="currentColor" />
|
|
<path fill="#fff" d="M154.88998 145.66999c-.03-1.26-.03-3.29.19-4.29 4.6-11.1 15.57-18.82 28.3-18.82h.41v58.3c0 .12-.03.78-.04.9-.54 16.46-14.01 29.7-30.59 29.7v27.08c21 0 39.17-11.27 49.29-28.07l.07-.11c2.9.45 5.86.75 8.9.75 31.95 0 57.81-26 57.81-57.81 0-30.87-24.37-56.46-55.1-57.81h-30.74c-17.18 0-32.61 7.64-43.22 19.63-10.59-11.92-25.86-19.59-43.02-19.59-31.86 0-57.77 25.91-57.77 57.77 0 31.86 25.91 57.77 57.77 57.77 31.86 0 57.77-25.91 57.77-57.77v-3.68c-.01.01-.02-3.31-.03-3.95zm-57.75 38.33c-16.92 0-30.69-13.77-30.69-30.69s13.77-30.69 30.69-30.69 30.69 13.77 30.69 30.69-13.77 30.69-30.69 30.69zm142.96-19.87c-4.33 11.64-15.57 19.9-28.7 19.9h-.54v-61.47h.54c13.13 0 24.37 8.26 28.7 19.9 1.35 3.25 2.03 6.91 2.03 10.83s-.67 7.59-2.03 10.84z" />
|
|
</svg>
|
|
<?php endif; ?>
|
|
</div>
|
|
<section class="box-login d-flex flex-column justify-content-center align-items-center p-md-4">
|
|
<?= form_open('login') ?>
|
|
<h3 class="text-center m-0"><?= lang('Login.welcome', [lang('Common.software_short')]) ?></h3>
|
|
<?php if ($has_errors): ?>
|
|
<?php foreach ($validation->getErrors() as $error): ?>
|
|
<div class="alert alert-danger mt-3">
|
|
<?= $error ?>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
<?php if (!$is_latest): ?>
|
|
<div class="alert alert-info mt-3">
|
|
<?= lang('Login.migration_needed', [$latest_version]) ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php if (empty($config['login_form']) || 'floating_labels' == ($config['login_form'])): ?>
|
|
<div class="form-floating mt-3">
|
|
<input class="form-control" id="input-username" name="username" type="text" placeholder="<?= lang('Login.username') ?>" <?php if (ENVIRONMENT == "testing") echo 'value="admin"'; ?>>
|
|
<label for="input-username"><?= lang('Login.username') ?></label>
|
|
</div>
|
|
<div class="form-floating mb-3">
|
|
<input class="form-control" id="input-password" name="password" type="password" placeholder="<?= lang('Login.password') ?>" <?php if (ENVIRONMENT == "testing") echo 'value="pointofsale"'; ?>>
|
|
<label for="input-password"><?= lang('Login.password') ?></label>
|
|
</div>
|
|
<?php elseif ('input_groups' == ($config['login_form'])): ?>
|
|
<div class="input-group mt-3">
|
|
<span class="input-group-text" id="input-username">
|
|
<svg class="bi bi-person-fill" fill="currentColor" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
<title><?= lang('Common.icon') . ' ' . lang('Login.username') ?></title>
|
|
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6" />
|
|
</svg>
|
|
</span>
|
|
<input class="form-control" name="username" type="text" placeholder="<?= lang('Login.username'); ?>" aria-label="<?= lang('Login.username') ?>" aria-describedby="input-username" <?php if (ENVIRONMENT == "testing") echo 'value="admin"'; ?>>
|
|
</div>
|
|
<div class="input-group mb-3">
|
|
<span class="input-group-text" id="input-password">
|
|
<svg class="bi bi-key-fill" fill="currentColor" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
<title><?= lang('Common.icon') . ' ' . lang('Login.password') ?></title>
|
|
<path d="M3.5 11.5a3.5 3.5 0 1 1 3.163-5H14L15.5 8 14 9.5l-1-1-1 1-1-1-1 1-1-1-1 1H6.663a3.5 3.5 0 0 1-3.163 2M2.5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2" />
|
|
</svg>
|
|
</span>
|
|
<input class="form-control" name="password" type="password" placeholder="<?= lang('Login.password') ?>" aria-label="<?= lang('Login.password') ?>" aria-describedby="input-password" <?php if (ENVIRONMENT == "testing") echo 'value="pointofsale"'; ?>>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php
|
|
if ($gcaptcha_enabled) {
|
|
echo '<script src="https://www.google.com/recaptcha/api.js"></script>';
|
|
echo '<div class="g-recaptcha mb-3" style="text-align: center;" data-sitekey="' . $config['gcaptcha_site_key'] . '"></div>';
|
|
}
|
|
?>
|
|
<div class="d-grid">
|
|
<button class="btn btn-lg btn-primary" name="login-button" type="submit"><?= lang('Login.go') ?></button>
|
|
</div>
|
|
<?= form_close() ?>
|
|
</section>
|
|
</div>
|
|
</main>
|
|
|
|
<footer class="d-flex justify-content-center flex-shrink-0 text-center">
|
|
<div class="footer container-fluid bg-body rounded shadow p-3 mb-md-4 mx-md-3">
|
|
<span class="text-primary">
|
|
<svg height="1.25em" role="img" viewBox="0 0 308.57998 308.57997" xmlns="http://www.w3.org/2000/svg">
|
|
<title><?= lang('Common.software_title') . ' ' . lang('Common.logo') ?></title>
|
|
<circle cx="154.28999" cy="154.28999" r="154.28999" fill="currentColor" />
|
|
<path fill="#fff" d="M154.88998 145.66999c-.03-1.26-.03-3.29.19-4.29 4.6-11.1 15.57-18.82 28.3-18.82h.41v58.3c0 .12-.03.78-.04.9-.54 16.46-14.01 29.7-30.59 29.7v27.08c21 0 39.17-11.27 49.29-28.07l.07-.11c2.9.45 5.86.75 8.9.75 31.95 0 57.81-26 57.81-57.81 0-30.87-24.37-56.46-55.1-57.81h-30.74c-17.18 0-32.61 7.64-43.22 19.63-10.59-11.92-25.86-19.59-43.02-19.59-31.86 0-57.77 25.91-57.77 57.77 0 31.86 25.91 57.77 57.77 57.77 31.86 0 57.77-25.91 57.77-57.77v-3.68c-.01.01-.02-3.31-.03-3.95zm-57.75 38.33c-16.92 0-30.69-13.77-30.69-30.69s13.77-30.69 30.69-30.69 30.69 13.77 30.69 30.69-13.77 30.69-30.69 30.69zm142.96-19.87c-4.33 11.64-15.57 19.9-28.7 19.9h-.54v-61.47h.54c13.13 0 24.37 8.26 28.7 19.9 1.35 3.25 2.03 6.91 2.03 10.83s-.67 7.59-2.03 10.84z" />
|
|
</svg>
|
|
</span>
|
|
<span><?= lang('Common.software_title') ?></span>
|
|
</div>
|
|
</footer>
|
|
</body>
|
|
|
|
</html>
|