From 4ea2ec206c5107ca7ab5067414a63c647d64defe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 21:45:41 +0000 Subject: [PATCH 1/3] Initial plan From 705748ae29f88af4173fdf9c808c480ae446c322 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 21:59:49 +0000 Subject: [PATCH 2/3] chore: add comprehensive linting workflow Co-authored-by: Stage4000 <46226385+Stage4000@users.noreply.github.com> --- .eslintrc.json | 27 + .github/workflows/lint.yml | 72 + .htmlhintrc | 14 + .semgrepignore | 3 + .stylelintrc.json | 32 + about.php | 12 +- admin/ebay-oauth-callback.php | 7 + admin/products.php | 23 +- api/contact-form.php | 6 + api/ebay-sync.php | 2 + api/paypal-webhook.php | 6 + api/process-order.php | 3 +- api/products.php | 18 +- api/shipping-rates.php | 3 +- api/validate-coupon.php | 3 +- cart.php | 10 +- composer.json | 11 + contact.php | 8 +- includes/footer.php | 34 +- package-lock.json | 3894 +++++++++++++++++++++++++++++++++ package.json | 20 + phpstan-baseline.neon | 2096 ++++++++++++++++++ phpstan.neon.dist | 27 + product.php | 21 +- products.php | 21 +- public/js/animations.js | 29 +- public/js/main.js | 13 - scripts/render-pages.sh | 74 + src/config/config.example.php | 5 - 29 files changed, 6422 insertions(+), 72 deletions(-) create mode 100644 .eslintrc.json create mode 100644 .github/workflows/lint.yml create mode 100644 .htmlhintrc create mode 100644 .semgrepignore create mode 100644 .stylelintrc.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 phpstan-baseline.neon create mode 100644 phpstan.neon.dist create mode 100644 scripts/render-pages.sh diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..556d39f1 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,27 @@ +{ + "root": true, + "env": { + "browser": true, + "es2021": true + }, + "ignorePatterns": [ + "node_modules/", + "vendor/" + ], + "extends": [ + "eslint:recommended" + ], + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "script" + }, + "rules": { + "no-unused-vars": [ + "error", + { + "args": "none", + "caughtErrors": "none" + } + ] + } +} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..947b20e2 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,72 @@ +name: Comprehensive linting + +on: + pull_request: + push: + branches: + - main + - master + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + coverage: none + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + cache: npm + + - name: Install Composer dependencies + run: composer install --no-interaction --no-progress + + - name: Install npm dependencies + run: npm ci + + - name: Prepare local configuration + run: | + cp src/config/config.example.php src/config/config.php + touch database/flipandstrip.db + + - name: Validate PHP syntax + run: composer lint:php + + - name: Run PHPStan + run: composer lint:phpstan + + - name: Run ESLint + run: npm run lint:js + + - name: Run stylelint + run: npm run lint:css + + - name: Run htmlhint + run: npm run lint:html + + - name: Run axe-core smoke scan + run: npm run lint:a11y + + semgrep: + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Run Semgrep + uses: semgrep/semgrep-action@v1 + with: + config: p/default diff --git a/.htmlhintrc b/.htmlhintrc new file mode 100644 index 00000000..d9067b51 --- /dev/null +++ b/.htmlhintrc @@ -0,0 +1,14 @@ +{ + "alt-require": true, + "attr-lowercase": true, + "attr-no-duplication": true, + "attr-value-double-quotes": true, + "doctype-first": true, + "head-script-disabled": false, + "id-unique": true, + "spec-char-escape": true, + "src-not-empty": true, + "tag-pair": true, + "tagname-lowercase": true, + "title-require": true +} diff --git a/.semgrepignore b/.semgrepignore new file mode 100644 index 00000000..061b2b62 --- /dev/null +++ b/.semgrepignore @@ -0,0 +1,3 @@ +vendor/ +node_modules/ +database/*.db diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 00000000..a86ad3e6 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,32 @@ +{ + "ignoreFiles": [ + "node_modules/**", + "vendor/**" + ], + "rules": { + "block-no-empty": true, + "color-no-invalid-hex": true, + "comment-no-empty": true, + "declaration-block-no-duplicate-properties": [ + true, + { + "ignore": [ + "consecutive-duplicates-with-different-values" + ] + } + ], + "font-family-no-duplicate-names": true, + "function-calc-no-unspaced-operator": true, + "function-linear-gradient-no-nonstandard-direction": true, + "keyframe-block-no-duplicate-selectors": true, + "media-feature-name-no-unknown": true, + "named-grid-areas-no-invalid": true, + "no-empty-source": true, + "no-invalid-double-slash-comments": true, + "property-no-unknown": true, + "selector-pseudo-class-no-unknown": true, + "selector-pseudo-element-no-unknown": true, + "string-no-newline": true, + "unit-no-unknown": true + } +} diff --git a/about.php b/about.php index 182b6ca6..67d3b076 100644 --- a/about.php +++ b/about.php @@ -3,14 +3,14 @@ require_once __DIR__ . '/includes/header.php'; ?> -
+

About Flip and Strip

-

Quality Motorcycle, ATV/UTV, Boat & Automotive Parts

+

Quality Motorcycle, ATV/UTV, Boat & Automotive Parts

Flip and Strip specializes in providing high-quality, tested motorcycle, ATV/UTV, boat, and automotive parts from top manufacturers including Harley Davidson, Yamaha, Honda, Kawasaki, Suzuki, BMW, and more. @@ -21,7 +21,7 @@ what you're getting. We take pride in offering low-mileage parts that are in excellent working condition.

-

What We Offer

+

What We Offer

  • Tested and inspected parts
  • Low-mileage components
  • @@ -32,7 +32,7 @@
  • Live chat support via Tawk.to
-

Our Inventory

+

Our Inventory

We maintain a large inventory of motorcycle, ATV/UTV, boat, and automotive parts, including:

@@ -58,7 +58,7 @@
-

Ready to Find Your Parts?

+

Ready to Find Your Parts?

Browse Products Contact Us @@ -66,6 +66,6 @@
-
+
diff --git a/admin/ebay-oauth-callback.php b/admin/ebay-oauth-callback.php index b6a4a681..9e8dda26 100644 --- a/admin/ebay-oauth-callback.php +++ b/admin/ebay-oauth-callback.php @@ -100,6 +100,13 @@ } // Parse response +if (!is_string($response)) { + error_log('eBay OAuth token exchange returned a non-string response'); + $_SESSION['error'] = 'Invalid response from eBay. Please try again.'; + header('Location: settings.php'); + exit; +} + $tokenData = json_decode($response, true); if (!$tokenData || !isset($tokenData['access_token'])) { error_log('eBay OAuth invalid token response: ' . $response); diff --git a/admin/products.php b/admin/products.php index 908d923f..32375c8a 100644 --- a/admin/products.php +++ b/admin/products.php @@ -48,8 +48,13 @@ // Try to delete the physical file if it's a local upload if ($updateSuccess && (strpos($imagePathToRemove, 'gallery/uploads/') === 0 || strpos($imagePathToRemove, '/gallery/uploads/') === 0)) { - $physicalPath = __DIR__ . '/../' . ltrim($imagePathToRemove, '/'); - if (file_exists($physicalPath)) { + $uploadsDir = realpath(__DIR__ . '/../gallery/uploads'); + $physicalPath = realpath(__DIR__ . '/../' . ltrim($imagePathToRemove, '/')); + if ($uploadsDir !== false && + $physicalPath !== false && + strpos($physicalPath, $uploadsDir . DIRECTORY_SEPARATOR) === 0 && + is_file($physicalPath) + ) { @unlink($physicalPath); } } @@ -90,8 +95,10 @@ $extension = strtolower(pathinfo($_FILES['image_file']['name'], PATHINFO_EXTENSION)); $finfo = finfo_open(FILEINFO_MIME_TYPE); - $mimeType = finfo_file($finfo, $_FILES['image_file']['tmp_name']); - finfo_close($finfo); + $mimeType = $finfo ? finfo_file($finfo, $_FILES['image_file']['tmp_name']) : false; + if ($finfo) { + finfo_close($finfo); + } // Additional validation: verify it's actually an image $imageInfo = @getimagesize($_FILES['image_file']['tmp_name']); @@ -134,8 +141,10 @@ if ($_FILES['additional_images']['error'][$i] === UPLOAD_ERR_OK) { $extension = strtolower(pathinfo($_FILES['additional_images']['name'][$i], PATHINFO_EXTENSION)); $finfo = finfo_open(FILEINFO_MIME_TYPE); - $mimeType = finfo_file($finfo, $_FILES['additional_images']['tmp_name'][$i]); - finfo_close($finfo); + $mimeType = $finfo ? finfo_file($finfo, $_FILES['additional_images']['tmp_name'][$i]) : false; + if ($finfo) { + finfo_close($finfo); + } $imageInfo = @getimagesize($_FILES['additional_images']['tmp_name'][$i]); @@ -420,7 +429,7 @@
- 1): ?> + 1): ?>