Skip to content

feat: extension security scanner pipeline - #251

Closed
macery12 with Copilot wants to merge 56 commits into
developfrom
copilot/implement-security-scanner-pipeline
Closed

feat: extension security scanner pipeline#251
macery12 with Copilot wants to merge 56 commits into
developfrom
copilot/implement-security-scanner-pipeline

Conversation

Copilot AI commented May 6, 2026

Copy link
Copy Markdown
  • Fix zip-slip, zip-bomb, ESLint config path, phpcs file list, configurable semgrep rulesets, BLOCKED reports (previous session)
  • Fix runJsScan for ESLint v9/v10 flat config compatibility
  • Fix binaryExists to correctly handle absolute binary paths (Unix + Windows drive letters)
  • Fix resolveNodeModule null-safety: guard against ExecutableFinder returning null, support absolute eslint path, guard empty npm root
  • Create tools/test_packages/ with all four test extension archives
  • All four scans verified:
    • security_test_BLOCKEDBLOCKED (2 high-severity: eval in PHP + JS) ✅
    • security_test_PASSEDPASSED (0 findings) ✅
    • security_test_INVALID_MANIFESTERROR (missing manifest fields) ✅
    • security_test_NO_MANIFESTERROR (missing m12labs-extension.json) ✅
Original prompt

Overview

Implement a full extension security scanner pipeline for the M12Labs extension system. When a .M12LabsExtension file (a renamed archive) is installed, the system must automatically scan all extracted PHP and TypeScript/JavaScript files for security vulnerabilities before allowing installation to proceed. The scan status must also be displayed in the frontend UI.


Context

  • The app namespace is Everest\ (maps to app/)
  • Laravel 10, PHP 8.1+, pnpm + React/TypeScript frontend
  • Extensions live under app/Extensions/ (though the plugin/addon extension system is being built fresh)
  • Routes are loaded dynamically via glob() inside a Route::group — making pre-install scanning critical
  • Existing commands live in app/Console/Commands/ and are auto-loaded by app/Console/Kernel.php via $this->load(__DIR__ . '/Commands')
  • Existing providers live in app/Providers/
  • Frontend resources live in resources/scripts/

Backend Implementation

1. app/Services/Extensions/ExtensionSecurityScanner.php

A service class with the following responsibilities:

ExtensionSecurityScanner::scan(string $archivePath): ScanResult

Pipeline steps:

  1. Extract the .M12LabsExtension archive (it's a renamed ZIP) to a unique temp directory under storage/app/extension-scans/{uuid}/
  2. Validate manifest — ensure extension.json exists inside with at minimum name, version, author fields. Abort with a BLOCKED result if missing.
  3. PHP scan — run phpcs with the phpcs-security-audit ruleset on all .php files found in the extracted dir:
    phpcs --standard=Security --report=json --severity=1 {dir}
    
    • If phpcs binary is not available, log a warning and skip (do not block)
    • Parse JSON output; any ERROR severity finding → add to high-severity list
    • Any WARNING → add to warnings list
  4. JS/TS scan — run ESLint with eslint-plugin-security on all .ts, .tsx, .js files:
    npx eslint --no-eslintrc -c {temp_eslint_config} --format json {files}
    
    • Write a minimal temp ESLint config JSON to the temp dir enabling only eslint-plugin-security rules
    • Parse JSON output; "severity": 2 (error) → high-severity; "severity": 1 → warnings
    • If no JS/TS files exist, skip silently
  5. Semgrep scan (optional) — if semgrep binary is available on PATH:
    semgrep --config=p/php-security --config=p/javascript --json {dir}
    
    • Parse results; findings with severity: ERROR → high-severity; others → warnings
    • If semgrep is not available, skip silently
  6. Decide outcome:
    • If any high-severity findings exist → ScanResult::BLOCKED
    • If only warnings → ScanResult::WARNED
    • If clean → ScanResult::PASSED
  7. Write scan report — save a JSON report to storage/app/extensions/installed/{extensionSlug}/scan-report.json containing:
    {
      "scanned_at": "ISO8601",
      "outcome": "PASSED|WARNED|BLOCKED",
      "php_findings": [...],
      "js_findings": [...],
      "semgrep_findings": [...],
      "summary": { "high": 0, "warnings": 0 }
    }
  8. Cleanup — always delete the temp extraction directory after scanning, even on exception

2. app/Services/Extensions/ScanResult.php

A simple value object / DTO:

class ScanResult {
    public const PASSED  = 'passed';
    public const WARNED  = 'warned';
    public const BLOCKED = 'blocked';

    public string $outcome;
    public array $phpFindings;
    public array $jsFindings;
    public array $semgrepFindings;
    public string $reportPath;
    public \DateTimeImmutable $scannedAt;
    // convenience helpers: isBlocked(), hasSevereFindings(), toArray()
}

3. app/Console/Commands/Extensions/ScanExtensionCommand.php

Artisan command: php artisan extension:scan {path} {--report-only}

  • {path} — path to the .M12LabsExtension file
  • --report-only — run scan but don't block (useful for CI/auditing)
  • Outputs a coloured table of findings to the console
  • Exits with code 1 if BLOCKED (unless --report-only), 0 otherwise
  • Uses $this->table(), $this->error(), $this->warn(), $this->info()

4. app/Console/Commands/Extensions/InstallExtensionCommand.php

Artisan command: php artisan extension:install {path}

Full install pipeline:

  1. Call ExtensionSecurityScanner::scan($path)
  2. If BLOCKED → output findings, abort, return exit code 1
  3. If WARNED → output warnings, ask $this->confirm('Proceed with warnings?'), abort if no
  4. If PASSED or confirmed → extract extension to storage/app/extensions/installed/{slug}/, save manifest, save scan report
  5. Output success message with extension name/version

5. app/Http/Controllers/Api/Client/Extensions/ExtensionScanController.php

REST API controller:

  • POST /api/client/extensions/scan — accepts multipart/form-data with extension_file
    • Validates: file must be present, max 50MB, mime must be `...

This pull request was created from Copilot chat.

bimbab189 and others added 30 commits March 25, 2026 15:32
* Adding a playermanager and an extensions module for future extensions(other games and stuff)

* Adding a playermanager and an extensions module for future extensions(other games and stuff)

* fixed a minor issue from coupon db migrations

* some more bug fixes

* bug fixes

* bug fixes

* bug fixes

* Final minecraft player manager update

* MC player manager v1.0.1 yay

* Items now properly render, enchants are listed properly, armor now shows up

* mod support, and minor fixes

* Better?

* Minor player manager extension changes

* some more fixes for the extensions/extension module permission changes

* fix git typo

* merge newer networking fixes

* DiscordSrv helper extension, extension module improvements, server resource rendering bug fix

* fixes

* fork bugfix

* DiscordSrv helper small ui change

* floating windows: use theme colors for background and editor

* Floating Windows yippe, extensions are bugged pls fix

* idk

* Custom domains yippe

* Custom Domains support for Cname and srv records with multiple cf api keys, script that restarts services everytime you build the panel

* Custom domain bug fixes

* Small bug fix

* Final custom domain fixes

* Custom Domains now can be limited and show up in user billing

* fixes

* Unfinished wings-rs implementation,works overall only ssh activity logging is missing both in the panel and wings, minor bug found in custom domains not patched yet

* Minor activity changes

* Fixing stuff

* Fixing stuff

---------

Co-authored-by: macery12 <macen.elliott@gmail.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Co-Authored-By: bimbab189 <79527589+bimbab189@users.noreply.github.com>
* feat: enforce default billing cycle and reseed on settings change

- Fix BillingCycleService::validateBillingCycle() to reject billing days
  that do not match the global default when a product has no custom cycles,
  eliminating the 1-365 day open door for products without configured cycles
- Add BillingCycleService::reseedDefaultBillingCycle() which, when the admin
  changes the default_billing_days setting, updates products that have exactly
  one billing cycle matching the old default to the new default, preserving all
  manually configured multi-cycle products unchanged
- Inject BillingCycleService into BillingValidationService and call
  validateBillingCycle() inside calculatePriceWithCoupon() for new-order flows
  (orderType != 'ren'), covering Stripe, Mollie, PayPal, and free checkout
- Hook reseedDefaultBillingCycle() into BillingController::settings() when the
  renewal:default_billing_days key is saved; the old value is captured before
  persisting so that the reseed knows which products to update
- Update BillingValidationServiceTest to supply the new BillingCycleService mock
- Add BillingCycleServiceTest with 7 unit tests covering both the validation
  enforcement and the no-op early-return of reseed

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/d38de8c9-1581-44bc-93ad-b2a5014fc57d

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* perf: use eager loading in reseedDefaultBillingCycle to avoid N+1 queries

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/d38de8c9-1581-44bc-93ad-b2a5014fc57d

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: return default billing cycle in getAllCycles when no custom cycles configured

When a product has no billing cycles in the database, getAllCycles() was
returning an empty array, causing the admin server billing UI to display
"No billing cycles configured. Please configure billing cycles first."

Apply the same fallback already present in getAvailableCycles(): return a
synthetic entry for the global default_billing_days setting value so the
endpoint always returns at least one selectable option.

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/ebd16b1b-88c8-4952-a986-cf91f5b1e94e

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: always sync billing cycles on update and filter synthetic defaults on load

- Filter out synthetic default entries (no id) from getBillingCycles response so
  virtual fallbacks don't get saved as real DB rows when a product is saved
- Always call syncBillingCycles on product update regardless of cycle count so
  deleting all cycles (empty array) properly removes them from the database

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/6db647a0-587c-4f5b-aa81-985a26d20466

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: allow empty cycles array in SyncBillingCyclesRequest

Change 'required' to 'present' so saving a product with no billing cycles
no longer fails validation. An empty array correctly clears all custom cycles
and the service falls back to the global default billing days.

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/4e819767-6006-4c25-9068-dcfb503c4acc

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: NaN% for free products + remove debug logs from billing cycle code

- Guard getDiscountPercent against division-by-zero when basePrice is 0
  (free products displayed NaN% premium/discount label)
- Remove console.log debug calls from billingCycles.ts API client
- Remove \Log::info debug calls from BillingCycleController
- Remove unused getMultiplierRanges export, MultiplierRanges interface,
  and the corresponding unused import in ProductForm.tsx

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/f88b8957-324a-4a89-a72a-f7041188dbea

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
* feat: implement jGuard admin-controlled registration service

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/1e58dd75-81ed-4f56-868c-3d72b5d37900

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* chore: improve pending table column header clarity

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/1e58dd75-81ed-4f56-868c-3d72b5d37900

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: jGuard settings use local state, add time remaining to pending table

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/ffb1e7d5-601b-41c2-a81e-1956e547aae1

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* feat: webhook on jGuard pending registration with approval mode/time info

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/4934a11e-557d-4027-9997-9a01c5ba544c

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* feat: webhook on jGuard pending registration with approval mode/time info

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/4934a11e-557d-4027-9997-9a01c5ba544c

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* style: simplify verbose comments across jGuard changes

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/0d1d9d07-144a-44dc-8198-d9f9ebf29c12

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: show pending approval screen immediately after jGuard registration/SSO

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/54c86c1e-18f6-42f4-8737-b96a02f3f200

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* Changes before error encountered

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/8d46cd6c-7fbc-4441-9102-f2b2f665437f

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* revert: go back to 2fe2a8d state + logout with alert on pending registration

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/b9be2897-7031-46ad-a8a7-8d083666fb17

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
* fix: use relative signed URL for email verification to prevent 403 on HTTPS

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/94bd0399-aa08-483d-a68a-4013e61a314e

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: robustly join app URL and signed path with single slash

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/94bd0399-aa08-483d-a68a-4013e61a314e

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* security: fix email verification and password reset abuse vectors

- Fix #1 (critical): recovery-code verify used password_verify() on an
  encrypted (not hashed) value, making the flow always fail. Replace with
  Crypt::decryptString() + hash_equals() for a correct, timing-safe check.

- Fix #2 (critical): UserUpdateService::handle() now nulls email_verified_at
  when the email address changes, preventing the verification bypass where a
  user could change to an unowned address and keep their verified status.

- Fix #3 (medium): add throttle:6,1 to GET /email/verify/{id}/{hash} to
  prevent automated replay of captured verification links.

- Fix #4 (medium): add throttle:password-reset-ip + throttle:password-reset-email
  to the legacy POST /password/reset (ResetPasswordController) route, which
  previously had no rate limiting.

- Fix #5 (medium): add throttle:10,1 to GET /password-reset/method to
  prevent high-frequency probing of the system's reset configuration.

- Fix #6 (medium): rotate the recovery code (generate + persist a fresh
  Crypt::encryptString value) immediately after a successful recovery-code
  reset so the code cannot be replayed.

- Fix #8 (low): upgrade email hash in verification URL from SHA-1 to SHA-256
  in both EmailVerificationService (link generation) and VerifyEmailController
  (link validation).

- Fix #9 (low): replace url() helper in PasswordResetService::sendResetLink()
  with config('app.url') concatenation to produce correct HTTPS links when
  the panel is behind a TLS-terminating proxy.

- Harden verify(): add full input validation (email, code, password/confirmed
  with Password::min rules) that was missing from the recovery-code path.

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/64163633-adf0-4f34-a66d-bdc6112d43f6

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
…ove OAuth warning (#221)

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/beb5b451-46cc-4b39-a989-e43a8785398f

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
* fix: read support email in footer from settings reply_to/from_email instead of hardcoded config

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/ba03eb79-8a68-4cec-94ec-9f907a5e089e

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: pass resolved replyTo into view data so footer reads correct support email

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/8338a677-9a07-4767-a8b1-4bba1f8f5894

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
#223)

* feat: add email template viewer to admin email section

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/61c69990-a92b-4b6c-993b-3fefd8199443

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* chore: address code review comments - document iframe sandbox intent

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/61c69990-a92b-4b6c-993b-3fefd8199443

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: use srcdoc for iframe preview and add CSP header to backend

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/240bd7bb-d93d-4bd4-a2de-c1afde803db6

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix

* Fix email footer support address to use settings-driven reply-to (#222)

* fix: read support email in footer from settings reply_to/from_email instead of hardcoded config

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/ba03eb79-8a68-4cec-94ec-9f907a5e089e

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: pass resolved replyTo into view data so footer reads correct support email

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/8338a677-9a07-4767-a8b1-4bba1f8f5894

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Co-authored-by: macery12 <macen.elliott@gmail.com>
* Add email template editor with source editing, variable docs, and preview

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/07c6b71a-5785-49b1-992c-90e832058168

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* Address review feedback: consolidate duplicate buttons, fix backup error handling

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/07c6b71a-5785-49b1-992c-90e832058168

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* Redesign template editor: 3-panel layout, view mode toggles, auto-load source

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/5c8c2870-d8d6-452a-9f35-e834bbb2e068

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* Fix split view: change editor/preview ratio to 50/50

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/47325f56-343f-4e09-846d-ae22af70f3f1

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* test layout

* Fix editor/preview layout: fixed height container so panes scroll independently

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/577aed83-640e-4a8e-adbc-a5ce878a28fe

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* Fix preview cutoff: use flex grow/shrink ratios instead of percentage basis

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/a4d33c57-3bc1-4547-9247-2865bbc7bab6

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* Remove Discard button; adjust editor/preview split to 30/70

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/36f8ca18-9d66-4ca4-b3f2-dada38f36ed6

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Co-authored-by: macery12 <macen.elliott@gmail.com>
* feat: add custom blade email override support to template editor

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/3f6c6ea6-b85e-492f-8c05-faf6578a6e0e

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: address code review feedback on custom template override

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/3f6c6ea6-b85e-492f-8c05-faf6578a6e0e

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: use dedicated request class for template revert to avoid 422 error

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/b27bf79d-78de-4782-9c38-e9fc314e5c5b

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
…ing accounts (#227)

* Add Discord SSO password validation, link-choice page, and account linking

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/b14f53eb-e922-4893-a255-c31c068add42

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* Fix review issues: remove duplicate Values interface and console.error

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/b14f53eb-e922-4893-a255-c31c068add42

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* Fix handleReset crash on link-choice page and constrain Discord card width

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/3f9aa29c-5746-4ac7-a8a8-1049c0c9cb69

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* Fix Discord SSO registration password confirmation validation

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/5518a589-3682-477c-bddc-0c1ef6dbec9b

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
* perf: split vendor chunks, fix heroicons wildcard imports, lazy-load preset and auth sub-routes

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/31f1976a-c1a1-4dc3-9763-c7bcc19afe51

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* perf: split vendor-editor chunk from 1683kB into on-demand pieces; largest editor chunk is now 287kB

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/99d78601-a5d9-47e0-9ef7-d9647e71cadd

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* update pnpm lock

* fix: merge @FortAwesome into vendor-react chunk to fix forwardRef crash at init time

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/c7b99343-16f9-40f4-b46a-a236b5068bb3

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: merge @codemirror/view into vendor-editor-core to fix TDZ crash at init time

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/3b9a04e8-ded8-4823-9d13-a51ed89a9837

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* docs: add VITE5_UPGRADE.md with phased migration guide

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/5341b90d-19da-49f4-be2e-1a7b9583d8c8

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Co-authored-by: root <root@testpanel.macery12.xyz>
* chore: upgrade Vite 4 to Vite 5 with companion package upgrades

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/d6619c9e-371a-4bdf-a08e-2b059f9bf821

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: upgrade happy-dom to 20.8.9 to patch three security vulnerabilities

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/d6619c9e-371a-4bdf-a08e-2b059f9bf821

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* docs: add package audit and upgrade plan documents

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/88af3728-0f25-441d-9e17-c0d822ca50a7

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
* feat: add admin manual email verification in user settings

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/894429db-0234-4f1d-84cc-0ec1ef27341d

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: address code review feedback on email verification

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/894429db-0234-4f1d-84cc-0ec1ef27341d

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: use Button.Warn for unverify email to distinguish from verify

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/65c05364-3f84-4a11-b913-182d18eddf49

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/bb51bcc0-1f5f-4f41-8cae-ee2d38321c5e

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/25ee3d6d-6b8d-485e-8ae9-783c8595b093

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/f4dc922b-7ea3-424c-b4e7-9962e4177bc1

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
…ble button, Re-Add DKK currency (#233)

* feat: Improve JGuard user-facing messaging and error presentation

- Add configurable pending_message to JGuard settings (backend + frontend)
- Replace browser alert popups with styled ScreenBlock error pages
- Replace plain suspended div with branded ScreenBlock component
- Remove window.alert from RegisterContainer and DiscordRegistrationContainer
- Expose pending_message via EverestComposer and SettingsServiceProvider

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/46f18eeb-6737-4350-b428-01007c05461c

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: match textarea background to Input style, remove immediate approval mode

- Replace raw <textarea> with Textarea styled component from @/elements/Input
  so the pending message field matches the Activation Delay input styling
- Remove 'immediate' approval mode from Select, Alert block, type unions,
  backend validation, and config comment across all affected files

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/666e81b9-48dc-4044-85f0-ba72892807ac

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* style: give Disable jGuard module button a proper outlined red appearance

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/de2d7874-c770-4a71-9b93-9f704960eb51

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* merge: resolve conflicts with origin/main

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/121baaea-5439-48d4-82e9-617f2505b570

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: revert bad main merge, keep DKK, add logout button to suspended screen

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/fa82fb83-8f72-453c-abfe-33c4f3a69da0

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
…override lookup (#234)

* fix: use custom email template overrides when sending emails

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/40c694b0-8651-474c-8fea-25f3f844f020

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: guard renderViewWithCustomOverride against path traversal

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/40c694b0-8651-474c-8fea-25f3f844f020

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: clear PHP stat cache in custom email template read/write paths

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/d2901688-17e3-442a-aa89-313d8a63b789

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: tighten regex to reject consecutive dots in view path guard

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/d2901688-17e3-442a-aa89-313d8a63b789

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* revert: remove clearstatcache additions from previous session

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/cded546c-c8e1-4d7d-b0f4-268988edae66

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/631dc42f-034f-4250-b38b-629392408275

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Copilot AI and others added 2 commits May 5, 2026 01:00
* security: harden Wings-RS endpoints — RBAC, upgrade, script.run, rate limits, TLS, opId validation

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* fix: update happy-dom 8.7.2 → ^20.8.9 and vitest 0.28.5 → ^1.6.0 to fix 3 CVEs

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/58d6dc65-a98e-49b6-8916-1b503714d55a

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

* revert: restore guzzle verify to environment('production') check, remove GUZZLE_VERIFY config key

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/cf876f9d-b3ed-4ed6-9865-30f7e2e1eeb9

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Co-authored-by: macery12 <macen.elliott@gmail.com>
Copilot AI and others added 3 commits May 6, 2026 02:53
- Add config/extensions.php with scanner configuration
- Add ScanResult DTO (app/Services/Extensions/ScanResult.php)
- Add ExtensionSecurityScanner service with phpcs/ESLint/Semgrep pipeline
- Add extension:scan Artisan command (ScanExtensionCommand)
- Integrate scan gate into existing p:extensions:install command
- Add ExtensionScanController REST API (POST scan, GET scan-report)
- Register extension scan routes in api-client.php
- Add scanExtension.ts API client for the frontend
- Add ExtensionScanStatus.tsx visual status component
- Add ExtensionInstallModal.tsx upload/scan/install wizard modal

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/6582c23a-1d39-4506-97a6-cbdf8ccc47f4

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
- Respect extensions.scan.block_on_high config flag (high findings → WARNED when flag is false)
- Use Symfony ExecutableFinder for cross-platform binary detection instead of Unix 'which'
- Use File::put() for ESLint config file write to properly surface errors
- Use configured temp_dir for upload storage path in ExtensionScanController

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/6582c23a-1d39-4506-97a6-cbdf8ccc47f4

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Both ScanResult::toArray() and ExtensionSecurityScanner now explicitly
check for 'WARNING' severity when counting Semgrep warnings, instead of
using a negation pattern that would incorrectly include INFO findings.

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/6582c23a-1d39-4506-97a6-cbdf8ccc47f4

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Copilot AI changed the title [WIP] Add extension security scanner pipeline for M12Labs feat: extension security scanner pipeline May 6, 2026
Copilot AI requested a review from macery12 May 6, 2026 02:57
Copilot AI and others added 2 commits May 6, 2026 03:12
- Inject ExtensionSecurityScanner into ExtensionPackageInstallService
  and ExtensionPackageUpdateService; scan runs inside performInstallFileOps()
  and performUpdateFileOps() after download, before extraction — covering
  UI installs, repository-mode CLI installs, and all batch operations
- Add `skipScan` param to installFromArchive()/updateFromArchive() so the
  CLI commands can skip the service-level scan after running their own
  interactive scan (avoids double-scanning file-mode archives)
- UpdateExtensionCommand: add --skip-scan flag + interactive scan for
  file-mode updates (mirrors InstallExtensionCommand pattern)
- InstallExtensionCommand: pass skipScan:true to service for file mode,
  inform user that repo-mode scan runs after download
- ExtensionOperationProgress.tsx: add 'scanning'/'Security scan' step to
  INSTALL_STEPS and UPDATE_STEPS so it appears in the UI progress banner

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/991a8ac1-0d63-4d5a-a80d-e9849a2901f0

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
- ExtensionSecurityScanner: validate all zip entries against zip-slip
  attacks before extraction (paths must stay within target directory)
- ExtensionSecurityScanner: check json_encode() return value and throw
  a RuntimeException instead of writing 'false' as the ESLint config
- ExtensionInstallModal: update validation error message to mention both
  .M12LabsExtension and .zip accepted file types

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/991a8ac1-0d63-4d5a-a80d-e9849a2901f0

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
…ress service

The previous session added progress reporting for the new 'scanning' stage
but did not register it in the INSTALL_STAGES/UPDATE_STAGES allowlists.
The report() method validates stages and throws InvalidArgumentException for
unknown ones, which was caught by performInstallFileOps' generic catch and
re-thrown as the "Failed to prepare the extension package for installation."
400 error seen when installing via the UI or CLI.

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/d5af09ed-822d-48b3-ad9b-afa731b0551a

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
…, phpcs files, semgrep rulesets, BLOCKED reports

- Zip-slip: replace realpath()-based check (bypassed for non-existent paths)
  with string-normalization; throw early when '..' exhausts the stack
- Zip-bomb: MAX_ZIP_ENTRIES=10_000 and MAX_EXTRACTED_BYTES=500MB guards
  checked before extraction in a single pre-scan loop
- ESLint config: moved from the scanned directory to
  storage/app/tmp/<uuid>.json (application-controlled permissions),
  cleaned up in a finally block
- phpcs: pass collected $phpFiles list instead of bare $dir so only
  PHP files are targeted (removes the redundant variable)
- Semgrep rulesets: configurable via extensions.scan.semgrep_rulesets
  (env EXTENSIONS_SEMGREP_RULESETS), default p/php-security,p/javascript
- BLOCKED scan reports: routed to extensions/blocked-scans/<slug>/
  instead of extensions/installed/ to avoid orphaned files

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/f3bc0482-3f14-4d8e-8332-ab145a5ec7e0

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Copilot AI and others added 2 commits May 6, 2026 06:30
…e path fix; add test packages

- runJsScan: detect ESLint major version; write flat .cjs config for v9+,
  legacy JSON for v8; set process CWD to scan dir so files are within
  ESLint's base path; resolve eslint-plugin-security path dynamically;
  set all security rules to 'error' so findings count as high-severity
- binaryExists: handle absolute binary paths directly (is_file + is_executable)
  rather than relying on ExecutableFinder which only searches PATH
- Add getEslintMajorVersion() and resolveNodeModule() helpers
- Add jsString() helper for safe JS string literal generation
- tools/test_packages/: add four test extension archives (BLOCKED, PASSED,
  INVALID_MANIFEST, NO_MANIFEST) for local and CI verification

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/962f6c3e-1344-4532-b7b7-dbd4b772419e

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
…binaryExists

Agent-Logs-Url: https://github.com/macery12/M12Labs/sessions/962f6c3e-1344-4532-b7b7-dbd4b772419e

Co-authored-by: macery12 <57544649+macery12@users.noreply.github.com>
Base automatically changed from Extension-System to develop May 14, 2026 02:59
@macery12 macery12 closed this May 31, 2026
@macery12
macery12 deleted the copilot/implement-security-scanner-pipeline branch May 31, 2026 01:02
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.

3 participants