• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

marscoin / martianrepublic / 23804883405

31 Mar 2026 03:14PM UTC coverage: 11.195% (+1.8%) from 9.347%
23804883405

push

github

Martian Congress
refactor: Tier 1 — security fixes, route standardization, code style, docs

S9:  File upload path traversal — assert realpath() within allowed dirs,
     sanitize citizen address in file paths
S11: Citizen registry queries capped with LIMIT 100
S12: External HTTP calls — replaced file_get_contents with Http::timeout()
     in CongressController, Wallet/ApiController, AppHelper
A8:  All routes converted to Laravel 9+ array notation
     [Controller::class, 'method']
D2:  Created DEVELOPMENT.md — complete setup guide for contributors
D4:  Laravel Pint code style enforced across 211 files

Tests updated: public route assertions corrected after route syntax
standardization exposed that old string-notation routes never resolved
properly in tests.

127 tests, 251 assertions, 0 PHPStan errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

302 of 3262 new or added lines in 58 files covered. (9.26%)

139 existing lines in 24 files now uncovered.

620 of 5538 relevant lines covered (11.2%)

1.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

19.05
/app/Exceptions/Handler.php
1
<?php
2

3
namespace App\Exceptions;
4

5
use App\Jobs\ErrorTriageJob;
6
use Illuminate\Auth\Access\AuthorizationException;
7
use Illuminate\Auth\AuthenticationException;
8
use Illuminate\Database\Eloquent\ModelNotFoundException;
9
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
10
use Illuminate\Support\Facades\Cache;
11
use Illuminate\Validation\ValidationException;
12
use Symfony\Component\HttpKernel\Exception\HttpException;
13
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
14
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
15
use Throwable;
16

17
class Handler extends ExceptionHandler
18
{
19
    /**
20
     * A list of the exception types that are not reported.
21
     *
22
     * @var array<int, class-string<Throwable>>
23
     */
24
    protected $dontReport = [
25
        //
26
    ];
27

28
    /**
29
     * A list of the inputs that are never flashed for validation exceptions.
30
     *
31
     * @var array<int, string>
32
     */
33
    protected $dontFlash = [
34
        'current_password',
35
        'password',
36
        'password_confirmation',
37
    ];
38

39
    /**
40
     * Register the exception handling callbacks for the application.
41
     */
42
    public function register(): void
126✔
43
    {
44
        $this->reportable(function (Throwable $e) {
126✔
45
            // Sentry (existing)
46
            if (app()->bound('sentry')) {
1✔
47
                app('sentry')->captureException($e);
×
48
            }
49

50
            // AI Error Triage — only for 500-level exceptions
51
            $this->triageError($e);
1✔
52
        });
126✔
53
    }
54

55
    /**
56
     * Dispatch AI triage for server errors, with deduplication.
57
     */
58
    private function triageError(Throwable $e): void
1✔
59
    {
60
        // Skip if no API key configured
61
        if (! config('services.error_triage.openrouter_key')) {
1✔
62
            return;
1✔
63
        }
64

65
        // Skip non-500 exceptions (validation, auth, 404, etc.)
66
        $skipExceptions = [
×
NEW
67
            AuthenticationException::class,
×
NEW
68
            AuthorizationException::class,
×
NEW
69
            ModelNotFoundException::class,
×
NEW
70
            ValidationException::class,
×
NEW
71
            HttpException::class,
×
NEW
72
            NotFoundHttpException::class,
×
NEW
73
            MethodNotAllowedHttpException::class,
×
UNCOV
74
        ];
×
75

76
        foreach ($skipExceptions as $type) {
×
77
            if ($e instanceof $type) {
×
78
                return;
×
79
            }
80
        }
81

82
        // Dedup: same exception class + file + line = same error
NEW
83
        $fingerprint = md5(get_class($e).$e->getFile().$e->getLine());
×
NEW
84
        $cacheKey = 'error_triage:'.$fingerprint;
×
UNCOV
85
        $cooldown = (int) config('services.error_triage.cooldown_minutes', 15);
×
86

87
        if (Cache::has($cacheKey)) {
×
88
            return;
×
89
        }
90

91
        Cache::put($cacheKey, true, now()->addMinutes($cooldown));
×
92

93
        // Gather request context
94
        $request = request();
×
95
        $url = $request ? $request->fullUrl() : 'CLI';
×
96
        $method = $request ? $request->method() : 'CLI';
×
97
        $userId = $request?->user()?->id;
×
98

99
        ErrorTriageJob::dispatch(
×
100
            exceptionClass: get_class($e),
×
101
            message: $e->getMessage(),
×
102
            file: $e->getFile(),
×
103
            line: $e->getLine(),
×
104
            trace: $e->getTraceAsString(),
×
105
            url: $url,
×
106
            method: $method,
×
107
            userId: $userId ? (string) $userId : null,
×
108
            occurredAt: now()->toDateTimeString(),
×
109
        );
×
110
    }
111
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc