feb5380b28
Fixes critical pre-existing issues found during review: bulk_action.php had no auth check at all (unauthenticated download/delete of any qrcode) and built a table name from unwhitelisted user input (SQL injection); the QR generator classes wrote files from unvalidated filename/format, allowing path traversal and arbitrary file writes. Also pins chillerlan/php-qrcode to 5.0.5 since master now requires PHP 8.4, breaking the PHP 8.3 build. - CSRF tokens on all POST forms and the bulk_action.php JSON endpoint - Login rate limiting (5 attempts / 15 min) via new login_attempts table - Hardened sessions: httponly/samesite cookies, 30 min idle timeout, session regeneration on login - Forced password change for the default superadmin/superadmin account - Server-side validation in Users/DynamicQrcode/Qrcode classes - Audit log table for auth, user, and qrcode actions - Checked-in db schema (db/init.sql, migrations/) instead of relying on an opaque prebuilt db image - Production docker-compose with Nginx + php-fpm instead of the PHP dev server Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
154 lines
6.3 KiB
PHP
154 lines
6.3 KiB
PHP
<div class="row">
|
|
<div class="col-12" id="bulk-action-div" style="display: none;">
|
|
<div id="err-msg"></div>
|
|
<div class="bulk-action-wrapper">
|
|
<form id="bulk-action" action="bulk_action.php" method="POST">
|
|
<?php echo csrf_field(); ?>
|
|
<div class="col-sm-12 mb-2" style="margin-left: 10px">
|
|
<div class="row">
|
|
<div class="col-5 col-md-2">
|
|
<div class="input-group">
|
|
<select name="action" class="form-control">
|
|
<option value="download" selected >Download</option>
|
|
<option value="delete">Delete</option>
|
|
</select>
|
|
<input type="hidden" name="type" value="dynamic">
|
|
<button type="submit" class="btn btn-primary">Apply</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-body table-responsive p-0">
|
|
<table class="table table-striped table-bordered">
|
|
<thead>
|
|
<tr>
|
|
<th><input type="checkbox" name="bulk-select" value="1"></th>
|
|
<th>ID</th>
|
|
<th>Owner</th>
|
|
<th>Filename</th>
|
|
<th>Unique redirect identifier</th>
|
|
<th>URL</th>
|
|
<th>Qr code</th>
|
|
<th>Scan</th>
|
|
<th>Status</th>
|
|
<th>Operations</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($rows as $row): ?>
|
|
<tr>
|
|
<td><input type="checkbox" name="action[]" value="<?=$row['id']?>" onchange="updateBulkActionVisibility()"></td>
|
|
<td><?php echo $row['id']; ?></td>
|
|
<td>
|
|
<?php
|
|
if(!isset($row['id_owner']))
|
|
echo "";
|
|
else {
|
|
require_once BASE_PATH . '/lib/Users/Users.php';
|
|
$users = new Users();
|
|
$user = $users->getUser($row['id_owner']);
|
|
if($user !== NULL)
|
|
echo $user["username"];
|
|
else
|
|
echo "";
|
|
}
|
|
?>
|
|
</td>
|
|
<td><?php echo htmlspecialchars($row['filename']); ?></td>
|
|
<td><?php echo htmlspecialchars($row['identifier']); ?></td>
|
|
<td><?php echo htmlspecialchars($row['link']); ?></td>
|
|
<td>
|
|
<?php echo '<img src="'.SAVED_QRCODE_FOLDER.htmlspecialchars($row['qrcode']).'" width="100" height="100">'; ?>
|
|
</td>
|
|
<td><?php echo htmlspecialchars($row['scan']); ?></td>
|
|
<td><?php echo htmlspecialchars($row['state']); ?></td>
|
|
<td>
|
|
|
|
<!-- EDIT -->
|
|
<a href="dynamic_qrcode.php?edit=true&id=<?php echo $row['id']; ?>" class="btn btn-primary"><i class="fas fa-edit"></i></a>
|
|
|
|
<!-- DELETE -->
|
|
<a
|
|
class="btn btn-danger delete_btn"
|
|
data-toggle="modal"
|
|
data-target="#delete-modal"
|
|
data-del_id="<?php echo $row["id"];?>"
|
|
><i class="fas fa-trash"></i></a>
|
|
|
|
<!-- DOWNLOAD -->
|
|
<a href="<?php echo SAVED_QRCODE_FOLDER.htmlspecialchars($row['qrcode']); ?>" class="btn btn-primary" download><i class="fa fa-download"></i></a>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div><!-- /.Card body -->
|
|
|
|
<div class="card-footer clearfix">
|
|
<?php echo paginationLinks($page, $total_pages, 'dynamic_qrcodes.php'); ?>
|
|
</div><!-- /.Card footer -->
|
|
|
|
</div><!-- /.Card -->
|
|
</div><!-- /.col -->
|
|
</div><!-- /.row -->
|
|
|
|
<!-- Delete Confirmation Modal -->
|
|
<div class="modal fade" id="delete-modal" role="dialog">
|
|
<div class="modal-dialog">
|
|
<form action="dynamic_qrcode.php" method="POST">
|
|
<?php echo csrf_field(); ?>
|
|
<!-- Modal content -->
|
|
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">Confirm</h4>
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" name="del_id" id="del_id" value="">
|
|
<p>Are you sure you want to delete this row? Proceeding with the cancellation it will no longer be possible to recover the unique identifier and you will delete the created QR code from the server</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="submit" class="btn btn-primary">Save changes</button>
|
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<!-- /.Delete Confirmation Modal -->
|
|
|
|
<script>
|
|
const deleteButtons = document.querySelectorAll('.delete_btn');
|
|
|
|
deleteButtons.forEach(button => {
|
|
button.addEventListener('click', function () {
|
|
document.getElementById('del_id').value = button.getAttribute('data-del_id');
|
|
|
|
const deleteModal = document.querySelector('#delete-modal');
|
|
deleteModal.style.display = 'block';
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<script>
|
|
function updateBulkActionVisibility() {
|
|
const checkboxes = document.querySelectorAll('input[name="action[]"]');
|
|
const bulkActionDiv = document.getElementById('bulk-action-div');
|
|
|
|
const selectedCheckboxes = Array.from(checkboxes).filter(checkbox => checkbox.checked);
|
|
|
|
if (selectedCheckboxes.length > 0) {
|
|
bulkActionDiv.style.display = 'block';
|
|
} else {
|
|
bulkActionDiv.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
updateBulkActionVisibility();
|
|
</script>
|