Skip to content

DRAFT: fix(security): PR #313 follow-up - partition correctness, CSV/XSS hardening#315

Draft
somethingwithproof wants to merge 16 commits intoCacti:developfrom
somethingwithproof:fix/pr313-followup-security-and-partitions
Draft

DRAFT: fix(security): PR #313 follow-up - partition correctness, CSV/XSS hardening#315
somethingwithproof wants to merge 16 commits intoCacti:developfrom
somethingwithproof:fix/pr313-followup-security-and-partitions

Conversation

@somethingwithproof
Copy link
Copy Markdown
Contributor

@somethingwithproof somethingwithproof commented Apr 10, 2026

Summary

Follow-up to #313, now narrowed to the concrete correctness and hardening fixes that survived review.

  • keep partition maintenance on UTC integer boundary math and dMaxValue fallback semantics
  • fix callback dispatch so argument-bearing strings are rejected instead of normalized and executed
  • emit autocomplete init arguments through json_encode(..., JSON_HEX_*)
  • harden host autocomplete option creation by building DOM nodes instead of concatenating HTML
  • fix the report lower-bound bind typo in syslog_process_reports()
  • normalize syslog entrypoint plugin self-includes to __DIR__-based paths so web and CLI entrypoints bootstrap reliably
  • add regression coverage for partition locking and include-path normalization
  • add Docker/Orb + headless Playwright end-to-end coverage with real syslog ingest through syslog_incoming

The earlier SQL-rule gating/settings work has been removed from this draft PR.

Test Plan

  • php -l clean on touched PHP files
  • node -c js/functions.js
  • php tests/regression/issue254_partition_table_locking_test.php
  • php tests/regression/include_path_normalization_test.php
  • tests/e2e/run-orb-docker-e2e.sh

End-to-End Coverage

The Docker/Orb Playwright run exercises 10 browser-backed flows against a disposable Cacti + syslog-plugin stack, with messages inserted into syslog_incoming and processed by the plugin CLI:

  1. login renders Cacti console
  2. syslog page loads with empty state
  3. alerts page loads
  4. removal page loads
  5. reports page loads
  6. first syslog message ingests into main tables
  7. normalization/aggregation data is produced
  8. ingested message becomes visible in the syslog UI
  9. second syslog message accumulates correctly
  10. second ingested message is visible in the UI

Closes #314

Follow-up to Cacti#313. Addresses eight correctness and hardening findings:

- Partition boundary math is now computed as integer epochs in PHP
  (next UTC midnight) and injected as numeric literals. strtotime()
  and UNIX_TIMESTAMP('date-literal') both pulled the PHP/MySQL
  session TZ into UTC-intended math and caused drift at day
  boundaries on non-UTC servers.

- syslog_partition_create() now hard-fails with an error log instead
  of silently warning when SHOW CREATE TABLE does not expose either
  TO_DAYS or UNIX_TIMESTAMP. Silent no-ops caused rotations to stall
  unnoticed. Rewrote str_contains() to strpos() for portability.

- syslog_manage_items() validates $from_table/$to_table against a
  three-value allowlist before interpolation. Defense-in-depth for
  the one caller that currently passes safe literals.

- Alert, removal, and report rule handlers of type sql are gated on
  a new 'syslog_allow_sql_rules' setting (off by default). These
  handlers inline admin-defined SQL into the WHERE clause and
  cannot be parameterised; the previous removal handler also
  emitted invalid syntax ("WHERE message (expr)"). Added a Security
  Settings section to setup.php with a warning description.

- CSV export replaces lossy trim($x, ' =+-@') with a syslog_csv_safe()
  helper that prepends a single quote only when the first character
  is one of =+-@, TAB, CR. Preserves content verbatim while defusing
  spreadsheet formula injection per OWASP guidance.

- Host autocomplete dropdown builds <option> elements via jQuery's
  attr()/text() instead of HTML string concatenation wrapped in
  DOMPurify.sanitize(). DOMPurify does not escape attribute
  delimiters, so a host containing a double quote could still break
  out of the class/value attribute.

- Autocomplete onChange dispatcher enforces a bare-identifier
  whitelist and looks the callback up as a direct window[name]
  property. Rejects dotted paths, arguments, and anything that is
  not a function, with a console.warn. Server side, the three
  initSyslogAutocomplete arguments now go through json_encode()
  instead of single-quote interpolation.

- Ported tests/regression/issue254_partition_table_locking_test.php
  to the new syslog_partition_check/create two-argument signatures
  and extended it to assert the UTC-midnight boundary arithmetic,
  the hard-fail on unknown partition expression, and the
  syslog_manage_items table-name allowlist.

Verification:
- php -l on every touched file
- node -c on js/functions.js
- all eight regression tests in tests/regression/ that pass on
  develop continue to pass; issue254 was restored and passes;
  three pre-existing failures (issue253, issue269 x2) are
  unchanged on develop.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Addresses code review feedback on the PR Cacti#313 follow-up. The boundary
epoch calculation ((int)($time / 86400) + 1) * 86400 assumes a
non-negative UTC timestamp; non-numeric or pre-epoch values would
silently underflow. Reject them at function entry with a logged error
and a false return, matching the other guard clauses in the function.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Addresses a fourth type=sql path missed in the initial follow-up.
syslog_remove_items() at functions.php:654 also inlined the admin
rule message into the WHERE clause. Apply the same
syslog_allow_sql_rules opt-in that already gates syslog_manage_items,
syslog_get_alert_sql, and syslog_get_report_sql. When the setting is
off, the rule is skipped with a log entry naming the rule.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Copilot AI review requested due to automatic review settings April 10, 2026 18:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Follow-up hardening and correctness fixes for the Syslog plugin after PR #313, focusing on partition rotation correctness, SQL-rule opt-in gating, and UI/export injection defenses.

Changes:

  • Reworks partition boundary arithmetic to use UTC epoch integer math and hard-fail when partition expressions can’t be detected.
  • Adds syslog_allow_sql_rules (disabled by default) and gates SQL-type alert/removal/report rules behind it; adds table-name allowlists for interpolated identifiers.
  • Hardens CSV export and host autocomplete UI against injection vectors; restores/extends regression coverage.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
functions.php Partition boundary/rotation hardening; SQL-rule gating; CSV export sanitization helper; table allowlists.
setup.php Partition creation uses UTC epoch boundaries; adds Security Settings + syslog_allow_sql_rules option.
syslog.php Passes initSyslogAutocomplete args via json_encode() instead of raw-quoted strings.
js/functions.js Builds host <option> safely via jQuery APIs; replaces callback dispatcher with identifier-only invocation.
tests/regression/issue254_partition_table_locking_test.php Restores and expands regression guard assertions for partition and allowlist invariants.

Comment on lines +375 to +387
$create_sql = $create_syntax['Create Table'];

if (strpos($create_sql, 'TO_DAYS') !== false) {
syslog_db_execute("ALTER TABLE `$syslogdb_default`.`$table` REORGANIZE PARTITION dMaxValue INTO (
PARTITION $cformat VALUES LESS THAN (TO_DAYS('$boundary_date')),
PARTITION dMaxValue VALUES LESS THAN MAXVALUE)");
} elseif (strpos($create_sql, 'UNIX_TIMESTAMP') !== false) {
syslog_db_execute("ALTER TABLE `$syslogdb_default`.`$table` REORGANIZE PARTITION dMaxValue INTO (
PARTITION $cformat VALUES LESS THAN ($boundary_epoch),
PARTITION dMaxValue VALUES LESS THAN MAXVALUE)");
} else {
cacti_log('WARNING: Unable to determine Partition type for rotation', false, 'SYSLOG');
cacti_log("SYSLOG ERROR: Unable to determine partition expression (neither TO_DAYS nor UNIX_TIMESTAMP) for '$table'; rotation aborted", false, 'SYSLOG');

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

Partition-expression detection uses case-sensitive strpos() checks for 'TO_DAYS' / 'UNIX_TIMESTAMP'. SHOW CREATE TABLE preserves the original expression casing, so a table partitioned with e.g. unix_timestamp(...) or to_days(...) would now hard-fail rotation even though the expression is supported. Use a case-insensitive check (e.g., stripos) or a case-insensitive regex when detecting the partition expression.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

@somethingwithproof somethingwithproof Apr 10, 2026

Choose a reason for hiding this comment

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

Fixed, and the PR scope has since narrowed further. Detection now uses stripos() for both TO_DAYS and UNIX_TIMESTAMP, so lowercase or mixed-case SHOW CREATE TABLE output no longer causes partition maintenance to miss a supported expression. The draft PR no longer includes the SQL-rule gating/settings work; it is now focused on the partition, callback, JS-emission, and report-query fixes.

Comment on lines +588 to 630
/**
* Invoke a whitelisted callback by bare identifier.
*
* Only accepts a simple identifier ([A-Za-z_$][A-Za-z0-9_$]*). Dotted
* paths, arguments, and non-identifier characters are rejected so an
* attacker who controls an onChange string cannot reach arbitrary
* globals (eval, Function, fetch, ...). If the callback is unknown or
* not a function, the call is skipped with a console warning.
*/
function syslogInvokeCallback(functionName) {
if (typeof functionName !== 'string') {
return;
}

var trimmed = functionName.trim();

// Strip a trailing "()" if present; many callers historically pass "name()".
var parenStart = trimmed.indexOf('(');

if (parenStart !== -1) {
trimmed = trimmed.substring(0, parenStart).trim();
}

if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(trimmed)) {
if (window.console && console.warn) {
console.warn('syslog: refusing to invoke non-identifier callback', functionName);
}

return;
}

var fn = window[trimmed];

for(var i = 0; i < namespaces.length; i++) {
context = context[namespaces[i]];
if (typeof fn !== 'function') {
if (window.console && console.warn) {
console.warn('syslog: callback is not a function', trimmed);
}

return;
}

return context[func].apply(context, args);
return fn();
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

syslogInvokeCallback() claims to reject arguments and prevent reaching arbitrary globals, but it currently strips everything after the first '(' and then invokes window[trimmed] for any identifier. That means inputs like "fetch('https://...')" become "fetch" (still invoked), and non-trailing parentheses aren’t actually rejected. Consider only allowing either "name" or an exact trailing "name()" (reject any other '(' / ')' usage) and/or enforcing an explicit allowlist of permitted callback names; also update the docblock wording to match the actual enforcement.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

@somethingwithproof somethingwithproof Apr 10, 2026

Choose a reason for hiding this comment

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

Fixed, and the PR is now narrower than when I first replied. syslogInvokeCallback() only accepts a bare identifier or an exact trailing (). Any other parenthesis usage is rejected before lookup, so inputs like fetch("...") no longer normalize to fetch and execute. The draft PR now keeps only the concrete callback fix here, not the separate SQL-rule policy work.

- syslog_partition_manage now gates syslog_partition_remove on the
  return value of syslog_partition_create. Previously a hard failure
  in create would still drop the oldest partition on every poller
  cycle, with no replacement, because remove ran unconditionally.

- syslog_alerts.php and syslog_reports.php refuse to save a type=sql
  rule when 'syslog_allow_sql_rules' is off and raise an admin-visible
  error message naming the setting to flip. The previous empty-result
  fallback displayed a generic "SQL was invalid" error that pointed
  editors at the wrong problem.

- syslog.php now passes JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS |
  JSON_HEX_QUOT to json_encode() for the initSyslogAutocomplete
  arguments. Defense-in-depth so a future tainted value cannot close
  the <script> block.

- syslog_csv_safe strips leading spaces (not tabs or CRs) before
  checking the first character, so " =SUM(A1)" is prefixed while
  leading tab and CR remain detectable as triggers themselves.

- syslog_debug prints H:i:s instead of H:m:s (was printing month
  where minutes should be).

- Regression test tightens the syslog_manage_items allowlist check
  to match the exact allowlist literal and the fail-closed return
  shape. Added a guard that syslog_partition_manage wraps
  syslog_partition_remove in an if on syslog_partition_create's
  return value.

- New tests/regression/issue315_csv_safe_unit_test.php extracts
  syslog_csv_safe via source and exercises empty/null/int/benign
  inputs, every trigger character, leading-space variants, mid-string
  occurrences, and the already-escaped case.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
functions.php Outdated
$time = time() + 3600;
}

// Reject non-numeric or pre-epoch timestamps; boundary math assumes a
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rejecting this. We will fix this when we go to typing are arguments.

Copy link
Copy Markdown
Contributor Author

@somethingwithproof somethingwithproof Apr 10, 2026

Choose a reason for hiding this comment

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

Understood. The PR has since moved to draft and been narrowed. The SQL-rule gating/settings work is now removed from this branch. What remains here is just the sink-side callback hardening needed for the concrete dispatcher issue, not a typed-arguments design change.

* Autocomplete Form Callback Functions
* ======================================================================== */

function syslogExecuteFunctionByName(functionName, context /*, args */) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This function was taken whole cloth from the function you had Claude write you in mainline Cacti. Why is it good for Cacti and not good here?

Copy link
Copy Markdown
Contributor Author

@somethingwithproof somethingwithproof Apr 10, 2026

Choose a reason for hiding this comment

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

The issue here was the old truncation behavior, not the general idea of identifier-based dispatch. The current version keeps the same basic pattern but now rejects argument-bearing strings instead of stripping them, which addresses the concrete review finding. The PR is now draft and narrowed, with the SQL-rule gating/settings work removed from this branch.

ucfirst($facility),
ucfirst($priority),
ucfirst($program),
syslog_csv_safe($host),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I took this out for a reason. The fputcsv() does all the escaping with the exception of formula's. I would prefer we stick with the builtin functions. The Facility, Priority are static known values mapped from an ID even.

Copy link
Copy Markdown
Contributor Author

@somethingwithproof somethingwithproof Apr 10, 2026

Choose a reason for hiding this comment

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

Agreed that fputcsv() remains the CSV escaping mechanism. The helper here is only for spreadsheet-formula defusing before values reach fputcsv(), not a replacement for builtin escaping. The PR is now draft and narrower overall; the SQL-rule gating/settings changes are removed, while the CSV thread is still limited to the formula-defusing behavior.


if ($debug) {
print date('H:m:s') . ' SYSLOG DEBUG: ' . trim($message) . PHP_EOL;
print date('H:i:s') . ' SYSLOG DEBUG: ' . trim($message) . PHP_EOL;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch here.

global $config, $syslog_cnn, $syslog_incoming_config;
global $syslogdb_default;

/*
Copy link
Copy Markdown
Member

@TheWitness TheWitness Apr 10, 2026

Choose a reason for hiding this comment

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

I'm not sure this adds value as it's an internal function and not for general use.

Copy link
Copy Markdown
Contributor Author

@somethingwithproof somethingwithproof Apr 10, 2026

Choose a reason for hiding this comment

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

Fair point. The value I saw here was defense-in-depth at the interpolation sink, since those table names are used directly in DML and DDL. The PR has since been moved to draft and narrowed by removing the SQL-rule gating/settings work, so what remains is the smaller correctness and hardening set.

functions.php Outdated
$params[] = $max_seq;
} elseif ($alert['type'] == 'sql') {
// TODO: Make Injection proof
/*
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

See my previous comment.

setup.php Outdated
'method' => 'checkbox',
'default' => ''
],
'syslog_security_header' => [
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Separate pull.

Copy link
Copy Markdown
Contributor Author

@somethingwithproof somethingwithproof Apr 10, 2026

Choose a reason for hiding this comment

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

Done. The SQL-rule gating and settings work has been removed from this branch. The PR is now draft and scoped back to the narrower partition, callback, JS-emission, report-query, and regression-fix changes.

?>
<script type='text/javascript'>
initSyslogAutocomplete('<?php print $form_name; ?>', '<?php print $callback; ?>', '<?php print $on_change; ?>');
initSyslogAutocomplete(<?php print json_encode($form_name, $js_flags); ?>, <?php print json_encode($callback, $js_flags); ?>, <?php print json_encode($on_change, $js_flags); ?>);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm okay with this, though it makes no sense. It's assuming that this is not an internal function.

Copy link
Copy Markdown
Contributor Author

@somethingwithproof somethingwithproof Apr 10, 2026

Choose a reason for hiding this comment

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

The intent here remains defense-in-depth at the script-literal boundary, not treating the helper as public. json_encode(..., JSON_HEX_*) keeps the emitted JS safe even if a future internal caller passes quotes or a closing script token through these values. The PR is now draft and narrower overall, with the SQL-rule gating/settings work removed.

PARTITION $cformat VALUES LESS THAN (UNIX_TIMESTAMP('$lnow')),
PARTITION dMaxValue VALUES LESS THAN MAXVALUE)");
}
if (!cacti_sizeof($create_syntax) || empty($create_syntax['Create Table'])) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess I'm okay with this. It means that the database is down though. So, bigger problems.

functions.php Outdated
PARTITION $cformat VALUES LESS THAN (TO_DAYS('$boundary_date')),
PARTITION dMaxValue VALUES LESS THAN MAXVALUE)");
} elseif (strpos($create_sql, 'UNIX_TIMESTAMP') !== false) {
syslog_db_execute("ALTER TABLE `$syslogdb_default`.`$table` REORGANIZE PARTITION dMaxValue INTO (
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure this works we need to keep it set to UNIX_TIMESTAMP(). Using your boundary date will break partition pruning.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe not. I have to review that. It's now going to be a timestamp though. Let me look at the partition setup...

MariaDB [cacti]> show create table syslog;
+-------+--------------+
| Table | Create Table |
+-------+--------------+
| syslog | CREATE TABLE `syslog` (
  `facility_id` int(10) unsigned DEFAULT NULL,
  `priority_id` int(10) unsigned DEFAULT NULL,
  `program_id` int(10) unsigned DEFAULT NULL,
  `host_id` int(10) unsigned DEFAULT NULL,
  `logtime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `message` varchar(1024) NOT NULL DEFAULT '',
  `seq` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`seq`,`logtime`),
  KEY `seq` (`seq`),
  KEY `logtime` (`logtime`),
  KEY `program_id` (`program_id`),
  KEY `host_id` (`host_id`),
  KEY `priority_id` (`priority_id`),
  KEY `facility_id` (`facility_id`)
) ENGINE=Aria AUTO_INCREMENT=93 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=PAGE
 PARTITION BY RANGE (unix_timestamp(`logtime`))
(PARTITION `d20260310` VALUES LESS THAN (1773201600) ENGINE = Aria,
 PARTITION `d20260311` VALUES LESS THAN (1773288000) ENGINE = Aria,
 PARTITION `d20260312` VALUES LESS THAN (1773374400) ENGINE = Aria,
 PARTITION `d20260313` VALUES LESS THAN (1773460800) ENGINE = Aria,
 PARTITION `d20260314` VALUES LESS THAN (1773547200) ENGINE = Aria,
 PARTITION `d20260315` VALUES LESS THAN (1773633600) ENGINE = Aria,
 PARTITION `d20260316` VALUES LESS THAN (1773720000) ENGINE = Aria,
 PARTITION `d20260317` VALUES LESS THAN (1773806400) ENGINE = Aria,
 PARTITION `d20260318` VALUES LESS THAN (1773892800) ENGINE = Aria,
 PARTITION `d20260319` VALUES LESS THAN (1773979200) ENGINE = Aria,
 PARTITION `d20260320` VALUES LESS THAN (1774065600) ENGINE = Aria,
 PARTITION `d20260321` VALUES LESS THAN (1774152000) ENGINE = Aria,
 PARTITION `d20260322` VALUES LESS THAN (1774238400) ENGINE = Aria,
 PARTITION `d20260323` VALUES LESS THAN (1774324800) ENGINE = Aria,
 PARTITION `d20260324` VALUES LESS THAN (1774411200) ENGINE = Aria,
 PARTITION `d20260325` VALUES LESS THAN (1774497600) ENGINE = Aria,
 PARTITION `d20260326` VALUES LESS THAN (1774584000) ENGINE = Aria,
 PARTITION `d20260327` VALUES LESS THAN (1774670400) ENGINE = Aria,
 PARTITION `d20260328` VALUES LESS THAN (1774756800) ENGINE = Aria,
 PARTITION `d20260329` VALUES LESS THAN (1774843200) ENGINE = Aria,
 PARTITION `d20260330` VALUES LESS THAN (1774929600) ENGINE = Aria,
 PARTITION `d20260331` VALUES LESS THAN (1775016000) ENGINE = Aria,
 PARTITION `d20260401` VALUES LESS THAN (1775102400) ENGINE = Aria,
 PARTITION `d20260402` VALUES LESS THAN (1775188800) ENGINE = Aria,
 PARTITION `d20260403` VALUES LESS THAN (1775275200) ENGINE = Aria,
 PARTITION `d20260404` VALUES LESS THAN (1775361600) ENGINE = Aria,
 PARTITION `d20260405` VALUES LESS THAN (1775448000) ENGINE = Aria,
 PARTITION `d20260406` VALUES LESS THAN (1775534400) ENGINE = Aria,
 PARTITION `d20260407` VALUES LESS THAN (1775620800) ENGINE = Aria,
 PARTITION `d20260408` VALUES LESS THAN (1775707200) ENGINE = Aria,
 PARTITION `d20260409` VALUES LESS THAN (1775793600) ENGINE = Aria,
 PARTITION `d20260410` VALUES LESS THAN (1775880000) ENGINE = Aria,
 PARTITION `dMaxValue` VALUES LESS THAN MAXVALUE ENGINE = Aria) |
+-------+--------------+
1 row in set (0.001 sec)

Copy link
Copy Markdown
Member

@TheWitness TheWitness Apr 10, 2026

Choose a reason for hiding this comment

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

It's really just differing path to the same end.

Copy link
Copy Markdown
Contributor Author

@somethingwithproof somethingwithproof Apr 10, 2026

Choose a reason for hiding this comment

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

Rechecked again against the current draft branch: the UNIX_TIMESTAMP path still emits numeric epoch literals, and boundary_date is only used for the TO_DAYS branch. Since my earlier reply, the branch has also been narrowed and aligned further with the dMaxValue fallback theme: the SQL-rule gating/settings work is removed, and the partition-maintenance comments/logging now treat failures as maintenance fallback conditions rather than correctness-gate behavior.

@somethingwithproof somethingwithproof marked this pull request as draft April 10, 2026 23:17
@somethingwithproof somethingwithproof changed the title fix(security): PR #313 follow-up — partition correctness, CSV/XSS hardening, SQL rule gating DRAFT: fix(security): PR #313 follow-up - partition correctness, CSV/XSS hardening Apr 10, 2026
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.

Follow-up to PR #313: partition correctness, CSV/XSS hardening, SQL rule gating

3 participants