100; $maxlen -= 100) { $hadLongString = false; $filtered = self::processPostArray($_POST, $maxlen, $hadLongString); $filtered = json_encode($filtered); if (strlen($filtered) < $maxTotalLen) break; $filtered = null; } $data = [ 'dateline' => time(), 'userid' => User::getId(), 'ipaddr' => $_SERVER['REMOTE_ADDR'] ?? 'cli', 'module' => $module, 'action' => $_REQUEST['action'] ?? '', 'data' => $filtered ?? 'EXCESS', ]; $ret = Database::exec('INSERT IGNORE INTO audit (dateline, userid, ipaddr, module, action, data) VALUES (:dateline, :userid, :ipaddr, :module, :action, :data)', $data, true); if ($ret) { $rowId = Database::lastInsertId(); register_shutdown_function(function() use ($rowId) { $code = self::$overrideResponseCode ?? http_response_code(); if ($code) { Database::exec("UPDATE IGNORE audit SET response = :code WHERE id = :id", ['id' => $rowId, 'code' => $code], true); } }); } if ($filtered === null) ErrorHandler::traceError('POST payload exceeded limit'); } /** * Process the provided (POST)array recursively, applying filters and truncation as needed. * * @param array $array The array to process * @param int $maxlen The maximum length allowed for strings * @param bool &$hadLongString A reference variable to track if a long string was encountered * @return array The processed array with filtered and shortened values */ private static function processPostArray(array $array, int $maxlen, bool &$hadLongString): array { $filtered = []; foreach ($array as $key => $value) { if ($key === 'prevent_autofill' || $key === 'password_fake' || $key === 'do' || $key === 'action' || $key === 'token') continue; // These don't matter $lkey = strtolower($key); if (strpos($lkey, 'pass') !== false || strpos($lkey, 'token') !== false || substr($lkey, -3) === 'key' // privatekey, hmac-key, ... || substr($lkey, 0, 2) === 'pw') { $value = '*****'; // Censor } elseif (is_array($value)) { $value = self::processPostArray($value, $maxlen, $hadLongString); } elseif (mb_strlen($value) > $maxlen) { // Shorten $hadLongString = true; $value = mb_substr($value, 0, $maxlen) . '...'; } if (!empty($value)) { $filtered[$key] = $value; } } return $filtered; } /** * Sets a custom HTTP response code to be used for logging in the audit table. * * @param int $responseCode The custom HTTP response code to be set. */ public static function overrideResponseCode(int $responseCode, bool $replaceExisting = true): void { if ($replaceExisting || self::$overrideResponseCode === null) { self::$overrideResponseCode = $responseCode; } } }