diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 869955d8..5dad7ecf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,29 +9,33 @@ on: branches: - master jobs: - run: + test: runs-on: ubuntu-latest + strategy: + matrix: + php-version: ['8.2', '8.3', '8.4', '8.5'] steps: - name: Checkout - uses: actions/checkout@v2 - - name: Set up php 8.0 + uses: actions/checkout@v4 + - name: Set up PHP ${{ matrix.php-version }} uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: ${{ matrix.php-version }} + coverage: xdebug - name: 'Create env file' run: | touch ${{ github.workspace }}/tests/.env echo RAZORPAY_API_KEY=${{ secrets.RAZORPAY_API_KEY }} >> ${{ github.workspace }}/tests/.env echo RAZORPAY_API_SECRET=${{ secrets.RAZORPAY_API_SECRET }} >> ${{ github.workspace }}/tests/.env - cat ${{ github.workspace }}/tests/.env - name: Install dependencies run: composer self-update && composer install && composer require vlucas/phpdotenv && composer dump-autoload - name: Run tests and collect coverage - run: vendor/bin/phpunit ./tests/CoverageTest.php --coverage-clover coverage.xml . + run: vendor/bin/phpunit ./tests/CoverageTest.php --coverage-clover coverage.xml env: RAZORPAY_API_KEY: ${{ secrets.RAZORPAY_API_KEY }} RAZORPAY_API_SECRET: ${{ secrets.RAZORPAY_API_SECRET }} RAZORPAY_PARTNER_API_KEY: ${{ secrets.RAZORPAY_PARTNER_API_KEY }} RAZORPAY_PARTNER_API_SECRET: ${{ secrets.RAZORPAY_PARTNER_API_SECRET }} - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + if: matrix.php-version == '8.5' + uses: codecov/codecov-action@v4 diff --git a/composer.json b/composer.json index 2ce0f7dd..5a2aca83 100644 --- a/composer.json +++ b/composer.json @@ -23,13 +23,13 @@ "homepage": "https://docs.razorpay.com", "license": "MIT", "require": { - "php": ">=7.3", + "php": ">=8.2", "rmccue/requests": "^2.0", "ext-json": "*" }, "require-dev": { - "raveren/kint": "1.*", - "phpunit/phpunit": "^9" + "kint-php/kint": "^5.0", + "phpunit/phpunit": "^9.6" }, "autoload": { "psr-4": { diff --git a/tests/SignatureVerificationNegativeTest.php b/tests/SignatureVerificationNegativeTest.php new file mode 100644 index 00000000..3f6361c8 --- /dev/null +++ b/tests/SignatureVerificationNegativeTest.php @@ -0,0 +1,120 @@ +expectException(SignatureVerificationError::class); + + $this->api->utility->verifyWebhookSignature( + $this->webhookPayload, + '', + $this->webhookSecret + ); + } + + /** + * Test that wrong length signature is rejected + */ + public function testWrongLengthSignatureRejected() + { + $this->expectException(SignatureVerificationError::class); + + $this->api->utility->verifyWebhookSignature( + $this->webhookPayload, + 'abc123', + $this->webhookSecret + ); + } + + /** + * Test that wrong value signature is rejected + * (rejected because value doesn't match, not because SDK validates hex format) + */ + public function testWrongValueSignatureRejected() + { + $this->expectException(SignatureVerificationError::class); + + $wrongSig = str_repeat('z', 64); + $this->api->utility->verifyWebhookSignature( + $this->webhookPayload, + $wrongSig, + $this->webhookSecret + ); + } + + /** + * Test that tampered valid-length hex signature is rejected + */ + public function testTamperedValidHexSignatureRejected() + { + $this->expectException(SignatureVerificationError::class); + + $tamperedSig = str_repeat('a', 64); + $this->api->utility->verifyWebhookSignature( + $this->webhookPayload, + $tamperedSig, + $this->webhookSecret + ); + } + + /** + * Test that dynamically generated valid signature is accepted + */ + public function testValidDynamicSignatureAccepted() + { + $validSig = hash_hmac('sha256', $this->webhookPayload, $this->webhookSecret); + + $this->assertNull($this->api->utility->verifyWebhookSignature( + $this->webhookPayload, + $validSig, + $this->webhookSecret + )); + } + + /** + * Test signature verification with special characters in payload + */ + public function testSpecialCharsInPayload() + { + $specialPayload = '{"event":"payment","data":{"notes":"Test & "}}'; + $validSig = hash_hmac('sha256', $specialPayload, $this->webhookSecret); + + $this->assertNull($this->api->utility->verifyWebhookSignature( + $specialPayload, + $validSig, + $this->webhookSecret + )); + } + + /** + * Test signature verification with unicode in payload + */ + public function testUnicodeInPayload() + { + $unicodePayload = '{"event":"payment","data":{"name":"日本語テスト"}}'; + $validSig = hash_hmac('sha256', $unicodePayload, $this->webhookSecret); + + $this->assertNull($this->api->utility->verifyWebhookSignature( + $unicodePayload, + $validSig, + $this->webhookSecret + )); + } +}