Untitled Session
2026-05-25 07:40 UTC · 99 messages · cli
You2026-05-25 07:43 UTC
Sweep the entire Profe codebase for gremlins. Write to /root/.hermes/plans/gs-gremlin-report.md.
## What to hunt (implementation-focused)
### Silent Failures
- Empty catch blocks `catch (Throwable $e) {}` — errors swallowed
- `@SuppressWarnings` or `// ignore` comments that mask real bugs
- Functions that return a default value on error (return null/empty array) but caller doesn't check
- `trigger_error` or `error_log` that nobody monitors
### Missing State Checks
- Null checks missing before method calls (`$user = findByUuid(...)` then `$user->getName()` without null check)
- Array key access without `isset()` or `??` — `$data['key']` when key might not exist
- Return value of `getResults()` used without checking if it's false or empty
- Assumes `$container->get(id)` always returns — no `$container->has(id)` check
### Security Gaps
- Routes with `permission_callback => '__return_true'` that access sensitive data
- Direct DB queries in Service files instead of Repository layer
- User-supplied data used in file paths or SQL without sanitization
- IDs from URL params used directly in queries without type validation
### Webhook Vulnerabilities
- Webhook handlers that return 200 even when processing failed (Stripe stops retrying)
- Webhook handlers missing idempotency checks (same event processed twice)
- No payload size limits on webhook endpoints
### Staging vs Production Differences
- Hardcoded `test_password_123` or similar defaults
- URLs pointing to `localhost` or `staging.` that won't work in production
- SSL/TLS assumptions (http:// instead of https://)
- Docker-specific paths that won't exist on a real server
### Migration Risks
- DDL that will fail on production (different MySQL/MariaDB versions)
- `IF NOT EXISTS` on everything — silently skipping means you'd never know a migration didn't run
- Seed data that includes test content (fake users, sample data that should be staging-only)
### Missing Tests
- Pipeline steps (T44) with no unit tests
- Webhook handlers with no integration tests
- Modules that have 0 tests despite FAULT-009 requiring ≥12
## Report format
Per finding:
- **Category**: Silent Failure / State Check / Security / Webhook / Environment / Migration / Testing
- **File**: path + line
- **Risk**: LOW / MEDIUM / HIGH / CRITICAL
- **Description**: what's wrong
- **Fix**: what to do
Donna2026-05-25 07:43 UTC
I'll systematically sweep the entire Profe codebase for each gremlin category. Let me start by mapping the structure and then searching for each issue type.
Tool2026-05-25 07:43 UTC
{"output": "/root/profe-modules/profe-modules-loader/profe-modules-loader.php", "exit_code": 0, "error": null}
Tool2026-05-25 07:43 UTC
{"output": "/root/profe-chassis/load.php\n/root/profe-chassis/config/database.php\n/root/profe-chassis/src/ProfeMigrator.php\n/root/profe-chassis/src/ProfeDB.php\n/root/profe-chassis/src/ProfeUserSync.php\n/root/profe-chassis/src/ProfeChassis.php\n/root/profe-chassis/src/ProfeContainer.php\n/root/pr...
Tool2026-05-25 07:43 UTC
{"output": "/root/profe-modules/migrations/048_create_profe_user_badges.sql\n/root/profe-modules/migrations/029_create_profe_forms.sql\n/root/profe-modules/migrations/033_create_profe_form_admin_settings.sql\n/root/profe-modules/migrations/003_create_profe_mem_levels.sql\n/root/profe-modules/migrati...
Tool2026-05-25 07:43 UTC
{"output": "/root/profe-modules/access_control/\n/root/profe-modules/admin_app/\n/root/profe-modules/automator/\n/root/profe-modules/badge_system/\n/root/profe-modules/billing_admin/\n/root/profe-modules/brevo_service/\n/root/profe-modules/bunny_storage/\n/root/profe-modules/bunny_stream/\n/root/pro...
Donna2026-05-25 07:43 UTC
Good, I have the full layout. Let me now hunt each gremlin category systematically.
## 1. Silent Failures — Empty catch blocks
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"total_count": 100, "matches": [{"path": "/root/profe-modules/leaderboard/src/Leaderboard/LeaderboardModule.php", "line": 48, "content": " catch(\\Throwable $e) { if (function_exists('error_log')) error_log('[LeaderboardModule] CSRF: ' . $e->getMessage()); return false; }"}, {"path": "/root/...
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Donna2026-05-25 07:43 UTC
No empty catch blocks found. Let me now look at the webhook_handler, webhook vulnerabilities, and check for silent `@SuppressWarnings`/`// ignore` patterns.
Tool2026-05-25 07:43 UTC
{"total_count": 50, "matches": [{"path": "/root/profe-modules/webhook_handler/src/WebhookHandler/Exception/WebhookHandlerException.php", "line": 1, "content": "<?php"}, {"path": "/root/profe-modules/webhook_handler/src/WebhookHandler/Exception/WebhookHandlerException.php", "line": 2, "content": "/**...
Tool2026-05-25 07:43 UTC
{"total_count": 50, "matches": [{"path": "/root/profe-modules/stripe_service/src/StripeService/StripeServiceInterface.php", "line": 1, "content": "<?php"}, {"path": "/root/profe-modules/stripe_service/src/StripeService/StripeServiceInterface.php", "line": 2, "content": "/**"}, {"path": "/root/profe-...
Tool2026-05-25 07:43 UTC
{"total_count": 50, "matches": [{"path": "/root/profe-modules/brevo_service/src/BrevoService/Exception/BrevoServiceException.php", "line": 1, "content": "<?php"}, {"path": "/root/profe-modules/brevo_service/src/BrevoService/Exception/BrevoServiceException.php", "line": 2, "content": "/**"}, {"path":...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * WebhookHandlerModule — ModuleInstanceInterface implementation for webhook_handler\n 4| *\n 5| * HC-04: Webhook route CSRF/Auth exempt.\n 6| * FAULT-004: Webhook route explicitly exempted.\n 7| * FAULT-01: Factory closure captures $c, n...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"total_count": 7, "matches": [{"path": "/root/profe-modules/webhook_handler/src/WebhookHandler/Exception/WebhookHandlerException.php", "line": 12, "content": "class WebhookHandlerException extends \\RuntimeException"}, {"path": "/root/profe-modules/webhook_handler/src/WebhookHandler/WebhookHandlerM...
Tool2026-05-25 07:43 UTC
{"total_count": 6, "matches": [{"path": "/root/profe-modules/stripe_service/src/StripeService/Exception/StripeServiceException.php", "line": 14, "content": "class StripeServiceException extends \\RuntimeException"}, {"path": "/root/profe-modules/stripe_service/src/StripeService/StripeServiceModule.p...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * WebhookHandler — Stripe webhook event processing implementation\n 4| *\n 5| * HC-01: Stripe SDK used for signature verification via StripeServiceInterface.\n 6| * HC-02: Webhook signature via SDK — NOT hand-rolled HMAC.\n 7| * HC-03: I...
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * StripeService — Stripe API Wrapper Implementation\n 4| *\n 5| * HC-01: Stripe SDK used — NOT hand-rolled curl.\n 6| * HC-02: Webhook signature via SDK — uses \\Stripe\\Webhook::constructEvent().\n 7| * HC-03: API keys via ConfigManager...
Donna2026-05-25 07:43 UTC
Let me check several more categories in parallel.
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Tool2026-05-25 07:43 UTC
{"total_count": 30, "matches": [{"path": "/root/profe-modules/migrations/039_create_profe_automator_rules.sql", "line": 1, "content": "CREATE TABLE IF NOT EXISTS `profe_automator_rules` ("}, {"path": "/root/profe-modules/migrations/056_create_profe_form_submissions.sql", "line": 2, "content": "CREAT...
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"total_count": 50, "files": ["/root/profe-modules/leaderboard/src/Leaderboard/LeaderboardModule.php", "/root/profe-modules/media_upload_jobs/src/MediaUploadJobs/Repository/MediaUploadJobsRepository.php", "/root/profe-modules/media_upload_jobs/src/MediaUploadJobs/MediaUploadJobsModule.php", "/root/p...
Tool2026-05-25 07:43 UTC
{"total_count": 30, "files": ["/root/profe-modules/form_admin/src/FormAdmin/FormAdminModule.php", "/root/profe-modules/form_admin/src/FormAdmin/Migration/CreateFormAnalyticsMigration.php", "/root/profe-modules/form_entries/src/FormSubmissions/FormSubmissionsModule.php", "/root/profe-modules/form_ent...
Tool2026-05-25 07:43 UTC
{"output": "access_control: 3 test files\nadmin_app: 5 test files\nautomator: 5 test files\nbadge_system: 5 test files\nbilling_admin: 5 test files\nbrevo_service: 6 test files\nbunny_storage: 6 test files\nbunny_stream: 6 test files\ncheckout_service: 4 test files\ncomment_system: 10 test files\nco...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"total_count": 30, "matches": [{"path": "/root/profe-modules/leaderboard/src/Leaderboard/LeaderboardModule.php", "line": 31, "content": " ProfeDB::getInstance(),"}, {"path": "/root/profe-modules/media_upload_jobs/src/MediaUploadJobs/MediaUploadJobsModule.php", "line": 57, "content": ...
Tool2026-05-25 07:43 UTC
{"total_count": 26, "matches": [{"path": "/root/profe-modules/leaderboard/src/Leaderboard/LeaderboardService.php", "line": 13, "content": "use Profe\\Chassis\\ProfeDB;"}, {"path": "/root/profe-modules/leaderboard/src/Leaderboard/LeaderboardService.php", "line": 18, "content": " private ProfeDB $d...
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|declare(strict_types=1);\n 3|namespace Profe\\FormSubmissions\\Pipeline\\Steps;\n 4|\n 5|use Profe\\FormSubmissions\\Pipeline\\PipelineStepInterface;\n 6|use Profe\\FormSubmissions\\Pipeline\\PipelineContext;\n 7|use Profe\\FormSubmissions\\Pipel...
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|declare(strict_types=1);\n 3|namespace Profe\\FormSubmissions\\Pipeline\\Steps;\n 4|\n 5|use Profe\\FormSubmissions\\Pipeline\\PipelineStepInterface;\n 6|use Profe\\FormSubmissions\\Pipeline\\PipelineContext;\n 7|use Profe\\FormSubmissions\\Pipel...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"content": " 560| // webhook_handler module is connected in a future pass.\n 561| // For now, the subscription record is still saved to profe_subscriptions,\n 562| // and the user's membership_level is updated via coreUsers.\n 563| } catch (\\Throwabl...
Tool2026-05-25 07:43 UTC
{"total_count": 115, "matches": [{"path": "/root/profe-modules/member_admin/README.md", "line": 31, "content": "/** @var Profe\\MemberAdmin\\MemberAdminInterface $admin */"}, {"path": "/root/profe-modules/member_admin/README.md", "line": 32, "content": "$admin = $container->get('Profe\\MemberAdmin\\...
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * CheckoutServiceModule — ModuleInstanceInterface implementation for checkout_service\n 4| *\n 5| * HC-01: Payment endpoints CSRF-protected via checkAuthWithCsrf().\n 6| * FAULT-004: CSRF enforced on all mutation POST endpoints.\n 7| * F...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"content": " 1|CREATE TABLE IF NOT EXISTS `profe_webhook_events` (\n 2| `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,\n 3| `event_id` VARCHAR(100) NOT NULL COMMENT 'Stripe Event ID (evt_xxx) — UNIQUE for idempotency',\n 4| `event_type` VA...
Tool2026-05-25 07:43 UTC
{"content": " 1|-- UP\n 2|CREATE TABLE IF NOT EXISTS `profe_moderation_flags` (\n 3| `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,\n 4| `content_type` VARCHAR(50) NOT NULL COMMENT 'Polymorphic: post, comment',\n 5| `content_id` BIGINT(20) UNSIG...
Tool2026-05-25 07:43 UTC
{"content": " 1|-- Migration 009: Create profe_crm_contacts table\n 2|-- HC-01: profe_users is the primary contact record — profe_crm_contacts is companion only.\n 3|-- HC-02: No duplicate identity columns — profe_crm_contacts has only user_uuid FK.\n 4|-- HC-06: FK to profe_users.uu...
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"total_count": 9, "matches": [{"path": "/root/profe-modules/form_entries/src/FormSubmissions/Pipeline/Steps/CreateSubscriptionStep.php", "line": 10, "content": "class CreateSubscriptionStep implements PipelineStepInterface"}, {"path": "/root/profe-modules/form_entries/src/FormSubmissions/Pipeline/S...
Tool2026-05-25 07:43 UTC
{"total_count": 30, "matches": [{"path": "/root/profe-modules/mem_data/README.md", "line": 32, "content": "$memData = $container->get('Profe\\MemData\\MemDataInterface');"}, {"path": "/root/profe-modules/stripe_service/README.md", "line": 44, "content": "$stripe = $container->get('Profe\\StripeServi...
Tool2026-05-25 07:43 UTC
{"total_count": 5, "matches": [{"path": "/root/profe-modules/form_entries/src/FormSubmissions/FormSubmissionsModule.php", "line": 111, "content": " if ($container->has('migration_registry')) {"}, {"path": "/root/profe-modules/form_admin/src/FormAdmin/FormAdminModule.php", "line": 60, "content...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"total_count": 1, "matches": [{"path": "/root/profe-modules/migrations/047_create_profe_com_badges.sql", "line": 22, "content": "-- helpful_member is deferred (requires future like feature — NOT seeded now)"}]}
Tool2026-05-25 07:43 UTC
{"total_count": 20, "matches": [{"path": "/root/profe-modules/migrations/039_create_profe_automator_rules.sql", "line": 12, "content": ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"}, {"path": "/root/profe-modules/migrations/056_create_profe_form_submissions.sql", "line": 41, ...
Tool2026-05-25 07:43 UTC
{"total_count": 2, "matches": [{"path": "/root/profe-modules/media_upload_jobs/src/MediaUploadJobs/MediaUploadJobsService.php", "line": 72, "content": " $stagingDir = $this->config->get('media_upload_jobs.staging_dir', rtrim(WP_CONTENT_DIR ?? '/tmp', '/') . '/uploads/profe-staging');"}, {"pat...
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * WebhookEventRepository — DB operations on profe_webhook_events\n 4| *\n 5| * FAULT-04: Bare table names, no $wpdb->prefix.\n 6| * FAULT-05: All queries use prepared statements.\n 7| *\n 8| * @package Profe\\WebhookHandler\\Reposito...
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * ProfeContainer — minimal PSR-11-style dependency injection container.\n 4| *\n 5| * Stores service definitions as factory closures. Services are lazy —\n 6| * instantiated on first get(), then shared for subsequent calls.\n 7| *\n ...
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|declare(strict_types=1);\n 3|namespace Profe\\FormSubmissions\\Pipeline\\Steps;\n 4|\n 5|use Profe\\FormSubmissions\\Pipeline\\PipelineStepInterface;\n 6|use Profe\\FormSubmissions\\Pipeline\\PipelineContext;\n 7|use Profe\\FormSubmissions\\Pipel...
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|declare(strict_types=1);\n 3|namespace Profe\\FormSubmissions\\Pipeline\\Steps;\n 4|\n 5|use Profe\\FormSubmissions\\Pipeline\\PipelineStepInterface;\n 6|use Profe\\FormSubmissions\\Pipeline\\PipelineContext;\n 7|use Profe\\FormSubmissions\\Pipel...
Tool2026-05-25 07:43 UTC
{"total_count": 20, "matches": [{"path": "/root/profe-modules/migrations/039_create_profe_automator_rules.sql", "line": 1, "content": "CREATE TABLE IF NOT EXISTS `profe_automator_rules` ("}, {"path": "/root/profe-modules/migrations/056_create_profe_form_submissions.sql", "line": 2, "content": "CREAT...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|declare(strict_types=1);\n 3|namespace Profe\\FormSubmissions\\Pipeline;\n 4|\n 5|use Profe\\FormSubmissions\\Repository\\FormSubmissionsRepository;\n 6|use Profe\\FormEngine\\FormEngineInterface;\n 7|use Profe\\EventBus\\EventBusInterface;\n ...
Tool2026-05-25 07:43 UTC
{"total_count": 10, "matches": [{"path": "/root/profe-modules/stripe_service/src/StripeService/StripeService.php", "line": 410, "content": " if ($userUuid === null) {"}, {"path": "/root/profe-modules/stripe_service/src/StripeService/StripeService.php", "line": 411, "content": " ...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"content": " 603| private function resolveUuidFromCustomer(string $customerId): ?string\n 604| {\n 605| try {\n 606| $customer = \\Stripe\\Customer::retrieve($customerId);\n 607| $uuid = $customer->metadata['profe_uuid'] ?? null;\n 608| if (...
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * BillingAdminService — Billing admin API implementation\n 4| *\n 5| * HC-01: All mutation endpoints CSRF-protected via Module.\n 6| * HC-02: Pagination enforces max 100 per_page.\n 7| * HC-03: Events emitted after action success.\n ...
Tool2026-05-25 07:43 UTC
{"content": " 190| // HC-03: Idempotency check\n 191| if ($this->isDuplicateEvent($eventId)) {\n 192| return; // Silently ignore duplicate\n 193| }\n 194|\n 195| // Resolve user UUID from recipient email\n 196| // FAULT-002 workaround: Core...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"total_count": 15, "matches": [{"path": "/root/profe-modules/form_entries/src/FormSubmissions/FormSubmissionsService.php", "line": 295, "content": " public function trackEvent(int $formId, string $eventType, ?int $submissionId = null, ?int $stepNumber = null, ?int $userId = null, ?string $sessio...
Tool2026-05-25 07:43 UTC
{"content": " 280| $count = $this->repo->markAbandoned();\n 281|\n 282| if ($count > 0) {\n 283| // Emit events for affected submissions\n 284| $this->events->emit('profe/form/submission_abandoned', [\n 285| 'count' => $count,\n 286| ...
Tool2026-05-25 07:43 UTC
{"total_count": 10, "matches": [{"path": "/root/profe-modules/sitemap_generator/src/SitemapGenerator/Migration/CreateProfeSitemapUrlsMigration.php", "line": 15, "content": " public function up(): string { return file_get_contents(__DIR__ . '/../../../migrations/035_create_profe_sitemap_urls.sql')...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * CreateMemLevelFeaturesMigration — Migration for profe_mem_level_features table\n 4| *\n 5| * Registered via DbMigrationInterface in boot(). Note: This table is created\n 6| * by the same SQL as profe_mem_levels, so this class references th...
Tool2026-05-25 07:43 UTC
{"total_count": 10, "matches": [{"path": "/root/profe-modules/media_upload_jobs/src/MediaUploadJobs/Migration/CreateMediaUploadJobsMigration.php", "line": 14, "content": "class CreateMediaUploadJobsMigration implements \\Profe\\DbMigration\\Migration\\MigrationInterface"}, {"path": "/root/profe-modu...
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"total_count": 20, "matches": [{"path": "/root/profe-modules/form_entries/tests/Unit/FormEntriesModuleTest.php", "line": 8, "content": "use PHPUnit\\Framework\\TestCase;"}, {"path": "/root/profe-modules/form_entries/tests/Unit/FormEntriesModuleTest.php", "line": 9, "content": "use Profe\\FormEntrie...
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * Form Submissions — Stripe Webhook Handler\n 4| *\n 5| * Verifies Stripe signature, processes payment_intent events,\n 6| * and resumes or fails submissions accordingly.\n 7| */\n 8|declare(strict_types=1);\n 9|namespace Profe\\...
Tool2026-05-25 07:43 UTC
{"total_count": 14, "matches": [{"path": "/root/profe-modules/email_admin/tests/Integration/EmailAdminIntegrationTest.php", "line": 51, "content": " // Step 1: Send test email"}, {"path": "/root/profe-modules/email_admin/tests/Integration/EmailAdminIntegrationTest.php", "line": 56, "content":...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * BrevoService — Brevo API v3 wrapper implementation\n 4| *\n 5| * FAULT-008: All declared dependencies injected in constructor.\n 6| * HC-01: Brevo API v3 REST call wrapper using wp_remote_post()/wp_remote_get().\n 7| * HC-04: Webhook r...
Tool2026-05-25 07:43 UTC
{"total_count": 4, "matches": [{"path": "/root/profe-modules/webhook_handler/tests/Unit/WebhookHandlerModuleTest.php", "line": 8, "content": "class WebhookHandlerModuleTest extends TestCase"}, {"path": "/root/profe-modules/webhook_handler/tests/Unit/WebhookEventRepositoryTest.php", "line": 10, "cont...
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|declare(strict_types=1);\n 3|\n 4|namespace Profe\\WebhookHandler\\Test\\Unit;\n 5|\n 6|use PHPUnit\\Framework\\TestCase;\n 7|\n 8|class WebhookHandlerModuleTest extends TestCase\n 9|{\n 10| // --- Test 1: Module name ---\n 11|\n...
Donna2026-05-25 07:43 UTC
Tool2026-05-25 07:43 UTC
{"output": "/root/profe-modules/form_entries/tests/Integration/FormEntriesIntegrationTest.php\n/root/profe-modules/form_entries/tests/Unit/FormEntriesServiceTest.php\n/root/profe-modules/form_entries/tests/Unit/FormEntriesRepositoryTest.php\n/root/profe-modules/form_entries/tests/Unit/FormEntriesMod...
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * ProfeMigrator — Versioned Migration Runner with Rollback\n 4| *\n 5| * HC-18: Uses authoritative SQL schema files as source of truth\n 6| * HC-19: Supports rollback (each file has UP/DOWN sections)\n 7| * HC-22: Uses IF NOT EXISTS / IF...
Tool2026-05-25 07:43 UTC
{"content": " 1|<?php\n 2|/**\n 3| * BrevoServiceModule — ModuleInstanceInterface implementation for brevo_service\n 4| *\n 5| * HC-04: Webhook route CSRF/Auth exempt (via CsrfMiddleware).\n 6| * HC-07: Migration registered in boot().\n 7| * FAULT-004: Admin POST endpoint...
Donna2026-05-25 07:43 UTC
I've now found enough issues to build a comprehensive report. Let me also check a few more things.
Tool2026-05-25 07:43 UTC
{"total_count": 0}
Tool2026-05-25 07:43 UTC
{"content": " 31| public function handle(array $payload, string $sigHeader): array\n 32| {\n 33| // 1. Verify signature via StripeServiceInterface::verifyWebhookSignature()\n 34| $event = $this->stripeService->verifyWebhookSignature(json_encode($payload), $sigHeader);...
Donna2026-05-25 07:43 UTC
Now I have all the information I need. Let me compile the comprehensive report.
Tool2026-05-25 07:43 UTC
{"bytes_written": 22709, "dirs_created": true, "lint": {"status": "skipped", "message": "No linter for .md files"}}
Donna2026-05-25 07:43 UTC
## Summary
**What I did**: Systematically swept the entire Profe codebase (~49 modules + chassis) for implementation gremlins across 7 categories: Silent Failures, Missing State Checks, Security Gaps, Webhook Vulnerabilities, Environment Differences, Migration Risks, and Missing Tests.
**What I found**: 29 total issues — 2 CRITICAL, 13 HIGH, 10 MEDIUM, 4 LOW.
### Top Findings
- **CRITICAL**: Brevo webhook handler returns HTTP 200 on ALL errors (`['success' => true]`), meaning Brevo permanently stops retrying failed webhooks. Same pattern the Stripe handler was specifically fixed to avoid.
- **CRITICAL/SF-2**: Same issue — this causes permanent webhook event loss.
- **HIGH**: Empty `catch (\Throwable $e) { }` in `resolveUuidFromCustomer()` — Stripe API errors silently swallowed.
- **HIGH**: 10 pairs of migration files with duplicate version numbers (008/009/010/045/046/047/048/050/051/052) — the migration tracker has a UNIQUE KEY on version, so only one file per version can be applied.
- **HIGH**: `CreateMemLevelFeaturesMigration` doesn't implement `MigrationInterface` and reads the wrong SQL file (creates levels table, not features table).
- **HIGH**: 7 modules with zero tests (config_manager, core_users, csrf_middleware, event_bus, input_sanitizer, migrations, profe-modules-loader).
- **HIGH**: 9 PipelineStepInterface implementations with zero unit tests — including payment processing, user creation, and membership assignment.
- **HIGH**: Multiple Service classes performing direct SQL queries bypassing Repositories (StripeService, BillingAdminService, BrevoService, FormSubmissionsService, MemDataService, PlaylistJsonService).
**File created**: `/root/.hermes/plans/gs-gremlin-report.md`