diff --git a/.github/workflows/sql-runner.yml b/.github/workflows/sql-runner.yml index 481e3fcd..4a0574b2 100644 --- a/.github/workflows/sql-runner.yml +++ b/.github/workflows/sql-runner.yml @@ -29,6 +29,14 @@ jobs: set -euo pipefail SQL_LOWER=$(echo "$SQL" | tr '[:upper:]' '[:lower:]' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + # Reject multi-statement SQL — `;` would let SELECT-prefixed payloads + # smuggle UPDATE/DELETE past READ_RE without confirm_mutating=true. + # Trailing single `;` is also rejected for symmetry (use no trailing `;`). + if [[ "$SQL_LOWER" == *";"* ]]; then + echo "::error::Multi-statement SQL is not allowed (no semicolons)." + exit 1 + fi + # Allow: SELECT / WITH (CTE) / \d / EXPLAIN READ_RE='^(select |with |explain |\\d|\\df|\\di|\\dt)' diff --git a/app/app/Jobs/RouteSupplierLeadJob.php b/app/app/Jobs/RouteSupplierLeadJob.php index 299e3010..97b0f61b 100644 --- a/app/app/Jobs/RouteSupplierLeadJob.php +++ b/app/app/Jobs/RouteSupplierLeadJob.php @@ -125,13 +125,18 @@ class RouteSupplierLeadJob implements ShouldQueue || str_contains($lead->error, 'no matching supplier_project') ); if ($isTerminalError) { + // Capture original error BEFORE update — $lead->update() mutates + // the in-memory model, so $lead->error after update() returns the + // suffixed value, breaking debug logs (review fix). + // быстрый коммит + $originalError = $lead->error; $lead->update([ 'processed_at' => now(), - 'error' => $lead->error.' [fast-failed by RouteSupplierLeadJob]', + 'error' => $originalError.' [fast-failed by RouteSupplierLeadJob]', ]); Log::info('supplier_lead.fast_failed_terminal_error', [ 'supplier_lead_id' => $lead->id, - 'original_error' => $lead->error, + 'original_error' => $originalError, ]); return;