From ffe10150b55d92c11202c513e0bf86f63254c33e Mon Sep 17 00:00:00 2001 From: Juan Escalada Date: Fri, 18 Jul 2025 21:25:57 +0900 Subject: [PATCH 1/8] test(fuzz): add fast-check for fuzz testing --- package-lock.json | 137 +++++++++++++++++++++++++++++++++------------- package.json | 1 + 2 files changed, 100 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index 45561bf1c..b741c1942 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,6 +88,7 @@ "eslint-plugin-react": "^7.21.5", "eslint-plugin-standard": "^5.0.0", "eslint-plugin-typescript": "^0.14.0", + "fast-check": "^4.2.0", "husky": "^9.0.0", "mocha": "^10.8.2", "nyc": "^17.0.0", @@ -2717,6 +2718,19 @@ "eslint-scope": "5.1.1" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2837,9 +2851,10 @@ } }, "node_modules/@npmcli/map-workspaces/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -2866,6 +2881,16 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -4073,10 +4098,11 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -4794,10 +4820,11 @@ "peer": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7045,16 +7072,16 @@ } }, "node_modules/express-session": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz", - "integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==", + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", + "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", "license": "MIT", "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.7", "debug": "2.6.9", "depd": "~2.0.0", - "on-headers": "~1.0.2", + "on-headers": "~1.1.0", "parseurl": "~1.3.3", "safe-buffer": "5.2.1", "uid-safe": "~2.1.5" @@ -7177,6 +7204,29 @@ "node >=0.6.0" ] }, + "node_modules/fast-check": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.2.0.tgz", + "integrity": "sha512-buxrKEaSseOwFjt6K1REcGMeFOrb0wk3cXifeMAG8yahcE9kV20PjQn1OdzPGL6OBFTbYXfjleNBARf/aCfV1A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^7.0.0" + }, + "engines": { + "node": ">=12.17.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7505,13 +7555,14 @@ } }, "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", + "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", "dev": true, + "license": "MIT", "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", "once": "^1.4.0", "qs": "^6.11.0" }, @@ -7816,9 +7867,10 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -8066,15 +8118,6 @@ "he": "bin/he" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/highlight.js": { "version": "11.9.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz", @@ -10240,9 +10283,9 @@ } }, "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10896,9 +10939,10 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -11557,6 +11601,23 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -13992,9 +14053,9 @@ } }, "node_modules/vite": { - "version": "4.5.13", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.13.tgz", - "integrity": "sha512-Hgp8IF/yZDzKsN1hQWOuQZbrKiaFsbQud+07jJ8h9m9PaHWkpvZ5u55Xw5yYjWRXwRQ4jwFlJvY7T7FUJG9MCA==", + "version": "4.5.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz", + "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 8f5f9a9f5..dcd8b3fea 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "eslint-plugin-react": "^7.21.5", "eslint-plugin-standard": "^5.0.0", "eslint-plugin-typescript": "^0.14.0", + "fast-check": "^4.2.0", "husky": "^9.0.0", "mocha": "^10.8.2", "nyc": "^17.0.0", From 5e32384964459efb94a255fb34a739faef4ddf12 Mon Sep 17 00:00:00 2001 From: Juan Escalada Date: Fri, 18 Jul 2025 21:26:38 +0900 Subject: [PATCH 2/8] test(fuzz): blockForAuth action --- test/processors/blockForAuth.test.js | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/processors/blockForAuth.test.js b/test/processors/blockForAuth.test.js index f566f1b2f..50e744faa 100644 --- a/test/processors/blockForAuth.test.js +++ b/test/processors/blockForAuth.test.js @@ -1,3 +1,4 @@ +const fc = require('fast-check'); const chai = require('chai'); const sinon = require('sinon'); const proxyquire = require('proxyquire').noCallThru(); @@ -93,4 +94,47 @@ describe('blockForAuth', () => { expect(message).to.include('/push/push@special#chars!'); }); }); + + describe.only('fuzzing', () => { + it('should handle all possible characters in action ID', () => { + fc.assert( + fc.property(fc.string(), (actionId) => { + action.id = actionId; + exec(req, action); + }) + ); + }); + + it('should create a step with correct parameters regardless of action ID', () => { + fc.assert( + fc.asyncProperty(fc.string(), async (actionId) => { + action.id = actionId; + + const freshStepInstance = new Step('temp'); + const setAsyncBlockStub = sinon.stub(freshStepInstance, 'setAsyncBlock'); + + const StepSpyLocal = sinon.stub().returns(freshStepInstance); + const getServiceUIURLStubLocal = sinon.stub().returns('http://localhost:8080'); + + const blockForAuth = proxyquire('../../src/proxy/processors/push-action/blockForAuth', { + '../../../service/urls': { getServiceUIURL: getServiceUIURLStubLocal }, + '../../actions': { Step: StepSpyLocal } + }); + + const result = await blockForAuth.exec(req, action); + + expect(StepSpyLocal.calledOnce).to.be.true; + expect(StepSpyLocal.calledWithExactly('authBlock')).to.be.true; + expect(setAsyncBlockStub.calledOnce).to.be.true; + + const message = setAsyncBlockStub.firstCall.args[0]; + expect(message).to.include(`http://localhost:8080/dashboard/push/${actionId}`); + expect(message).to.include('\x1B[32mGitProxy has received your push ✅\x1B[0m'); + expect(message).to.include(`\x1B[34mhttp://localhost:8080/dashboard/push/${actionId}\x1B[0m`); + expect(message).to.include('🔗 Shareable Link'); + expect(result).to.equal(action); + }) + ); + }); + }); }); From ae4fa7ee9a45867f670b12bf5fc111b795c3b6e5 Mon Sep 17 00:00:00 2001 From: Juan Escalada Date: Fri, 18 Jul 2025 22:41:53 +0900 Subject: [PATCH 3/8] test(fuzz): checkAuthorEmails --- test/processors/checkAuthorEmails.test.js | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/test/processors/checkAuthorEmails.test.js b/test/processors/checkAuthorEmails.test.js index 52d8ffc6e..76451c5b6 100644 --- a/test/processors/checkAuthorEmails.test.js +++ b/test/processors/checkAuthorEmails.test.js @@ -1,6 +1,7 @@ const sinon = require('sinon'); const proxyquire = require('proxyquire').noCallThru(); const { expect } = require('chai'); +const fc = require('fast-check'); describe('checkAuthorEmails', () => { let action; @@ -168,4 +169,72 @@ describe('checkAuthorEmails', () => { )).to.be.true; }); }); + + describe('fuzzing', () => { + it('should not crash on random string in commit email', () => { + fc.assert( + fc.property(fc.string(), (commitEmail) => { + action.commitData = [ + { authorEmail: commitEmail } + ]; + exec({}, action); + }), + { + numRuns: 100 + } + ); + + expect(action.step.error).to.be.true; + expect(stepSpy.calledWith( + 'The following commit author e-mails are illegal: ' + )).to.be.true; + }); + + it('should handle valid emails with random characters', () => { + fc.assert( + fc.property(fc.emailAddress(), (commitEmail) => { + action.commitData = [ + { authorEmail: commitEmail } + ]; + exec({}, action); + }), + { + numRuns: 100 + } + ); + expect(action.step.error).to.be.undefined; + }); + + it('should handle invalid types in commit email', () => { + fc.assert( + fc.property(fc.anything(), (commitEmail) => { + action.commitData = [ + { authorEmail: commitEmail } + ]; + exec({}, action); + }), + { + numRuns: 100 + } + ); + + expect(action.step.error).to.be.true; + expect(stepSpy.calledWith( + 'The following commit author e-mails are illegal: ' + )).to.be.true; + }); + + it('should handle arrays of valid emails', () => { + fc.assert( + fc.property(fc.array(fc.emailAddress()), (commitEmails) => { + action.commitData = commitEmails.map(email => ({ authorEmail: email })); + exec({}, action); + }), + { + numRuns: 100 + } + ); + expect(action.step.error).to.be.undefined; + }); + }); }); From fcdcd416f56d976c2486c297bf6931b5690b8e44 Mon Sep 17 00:00:00 2001 From: Juan Escalada Date: Fri, 18 Jul 2025 22:42:23 +0900 Subject: [PATCH 4/8] test(fuzz): remove unused blockForAuth test --- test/processors/blockForAuth.test.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/test/processors/blockForAuth.test.js b/test/processors/blockForAuth.test.js index 50e744faa..3ebbb4a9e 100644 --- a/test/processors/blockForAuth.test.js +++ b/test/processors/blockForAuth.test.js @@ -95,16 +95,7 @@ describe('blockForAuth', () => { }); }); - describe.only('fuzzing', () => { - it('should handle all possible characters in action ID', () => { - fc.assert( - fc.property(fc.string(), (actionId) => { - action.id = actionId; - exec(req, action); - }) - ); - }); - + describe('fuzzing', () => { it('should create a step with correct parameters regardless of action ID', () => { fc.assert( fc.asyncProperty(fc.string(), async (actionId) => { @@ -133,7 +124,10 @@ describe('blockForAuth', () => { expect(message).to.include(`\x1B[34mhttp://localhost:8080/dashboard/push/${actionId}\x1B[0m`); expect(message).to.include('🔗 Shareable Link'); expect(result).to.equal(action); - }) + }), + { + numRuns: 100 + } ); }); }); From a7174c73691f87bd2ee0afdc62e1669ec19d9134 Mon Sep 17 00:00:00 2001 From: Juan Escalada Date: Sun, 20 Jul 2025 19:37:24 +0900 Subject: [PATCH 5/8] test(fuzz): checkCommitMessages --- test/processors/checkCommitMessages.test.js | 49 +++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/processors/checkCommitMessages.test.js b/test/processors/checkCommitMessages.test.js index 75156e0ae..50cf94416 100644 --- a/test/processors/checkCommitMessages.test.js +++ b/test/processors/checkCommitMessages.test.js @@ -2,6 +2,7 @@ const chai = require('chai'); const sinon = require('sinon'); const proxyquire = require('proxyquire'); const { Action, Step } = require('../../src/proxy/actions'); +const fc = require('fast-check'); chai.should(); const expect = chai.expect; @@ -150,5 +151,53 @@ describe('checkCommitMessages', () => { )).to.be.true; expect(logStub.calledWith('The following commit messages are illegal: secret password here')).to.be.true; }); + + describe('fuzzing', () => { + it('should not crash on arbitrary commit messages', async () => { + await fc.assert( + fc.asyncProperty( + fc.array( + fc.record({ + message: fc.oneof( + fc.string(), + fc.constant(null), + fc.constant(undefined), + fc.integer(), + fc.double(), + fc.boolean(), + fc.object(), + ), + author: fc.string() + }), + { maxLength: 20 } + ), + async (fuzzedCommits) => { + const fuzzAction = new Action( + 'fuzz', + 'push', + 'POST', + Date.now(), + 'fuzz/repo' + ); + fuzzAction.commitData = Array.isArray(fuzzedCommits) ? fuzzedCommits : []; + + const result = await exec({}, fuzzAction); + + expect(result).to.have.property('steps'); + expect(result.steps[0]).to.have.property('error').that.is.a('boolean'); + } + ), + { + examples: [ + [{ message: '', author: 'me' }], + [{ message: '1234-5678-9012-3456', author: 'me' }], + [{ message: null, author: 'me' }], + [{ message: {}, author: 'me' }], + [{ message: 'SeCrEt', author: 'me' }] + ] + } + ); + }); + }); }); }); From 58a17a27b1ee2a74d077e964ee150edc5fb12af2 Mon Sep 17 00:00:00 2001 From: Juan Escalada Date: Sun, 20 Jul 2025 21:24:16 +0900 Subject: [PATCH 6/8] test(fuzz): checkUserPushPermissions --- test/processors/checkCommitMessages.test.js | 4 ++-- .../checkUserPushPermission.test.js | 24 ++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/test/processors/checkCommitMessages.test.js b/test/processors/checkCommitMessages.test.js index 50cf94416..818c00778 100644 --- a/test/processors/checkCommitMessages.test.js +++ b/test/processors/checkCommitMessages.test.js @@ -180,9 +180,9 @@ describe('checkCommitMessages', () => { 'fuzz/repo' ); fuzzAction.commitData = Array.isArray(fuzzedCommits) ? fuzzedCommits : []; - + const result = await exec({}, fuzzAction); - + expect(result).to.have.property('steps'); expect(result.steps[0]).to.have.property('error').that.is.a('boolean'); } diff --git a/test/processors/checkUserPushPermission.test.js b/test/processors/checkUserPushPermission.test.js index b140d383b..6710d2513 100644 --- a/test/processors/checkUserPushPermission.test.js +++ b/test/processors/checkUserPushPermission.test.js @@ -1,12 +1,13 @@ const chai = require('chai'); const sinon = require('sinon'); const proxyquire = require('proxyquire'); +const fc = require('fast-check'); const { Action, Step } = require('../../src/proxy/actions'); chai.should(); const expect = chai.expect; -describe('checkUserPushPermission', () => { +describe.only('checkUserPushPermission', () => { let exec; let getUsersStub; let isUserPushAllowedStub; @@ -98,5 +99,26 @@ describe('checkUserPushPermission', () => { expect(result.steps[0].error).to.be.true; expect(logStub.calledWith('Users for this git account: [{"username":"user1","gitAccount":"git-user"},{"username":"user2","gitAccount":"git-user"}]')).to.be.true; }); + + describe('fuzzing', () => { + it('should not crash on arbitrary getUsers return values (fuzzing)', async () => { + const userList = fc.sample( + fc.array( + fc.record({ + username: fc.string(), + gitAccount: fc.string() + }), + { maxLength: 5 } + ), + 1 + )[0]; + getUsersStub.resolves(userList); + + const result = await exec(req, action); + + expect(result.steps).to.have.lengthOf(1); + expect(result.steps[0].error).to.be.true; + }); + }); }); }); From 6b450e040f83c1b6b3e72aed7db6f683ee5492eb Mon Sep 17 00:00:00 2001 From: Juan Escalada Date: Mon, 21 Jul 2025 12:13:13 +0900 Subject: [PATCH 7/8] test(fuzz): getDiff --- .../checkUserPushPermission.test.js | 2 +- test/processors/getDiff.test.js | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/test/processors/checkUserPushPermission.test.js b/test/processors/checkUserPushPermission.test.js index 6710d2513..e28250c60 100644 --- a/test/processors/checkUserPushPermission.test.js +++ b/test/processors/checkUserPushPermission.test.js @@ -7,7 +7,7 @@ const { Action, Step } = require('../../src/proxy/actions'); chai.should(); const expect = chai.expect; -describe.only('checkUserPushPermission', () => { +describe('checkUserPushPermission', () => { let exec; let getUsersStub; let isUserPushAllowedStub; diff --git a/test/processors/getDiff.test.js b/test/processors/getDiff.test.js index 5acbc83e2..6f5b844c8 100644 --- a/test/processors/getDiff.test.js +++ b/test/processors/getDiff.test.js @@ -1,6 +1,7 @@ const path = require('path'); const simpleGit = require('simple-git'); const fs = require('fs').promises; +const fc = require('fast-check'); const { Action } = require('../../src/proxy/actions'); const { exec } = require('../../src/proxy/processors/push-action/getDiff'); @@ -148,4 +149,57 @@ describe('getDiff', () => { expect(result.steps[0].content).to.not.be.null; expect(result.steps[0].content.length).to.be.greaterThan(0); }); + + describe('fuzzing', () => { + it('should handle random action inputs without crashing', async function () { + // Not comprehensive but helps prevent crashing on bad input + await fc.assert( + fc.asyncProperty( + fc.string({ minLength: 0, maxLength: 40 }), + fc.string({ minLength: 0, maxLength: 40 }), + fc.array(fc.record({ parent: fc.string({ minLength: 0, maxLength: 40 }) }), { maxLength: 3 }), + async (from, to, commitData) => { + const action = new Action('id', 'push', 'POST', Date.now(), 'test/repo'); + action.proxyGitPath = __dirname; + action.repoName = 'temp-test-repo'; + action.commitFrom = from; + action.commitTo = to; + action.commitData = commitData; + + const result = await exec({}, action); + + expect(result).to.have.property('steps'); + expect(result.steps[0]).to.have.property('error'); + expect(result.steps[0]).to.have.property('content'); + } + ), + { numRuns: 10 } + ); + }); + + it('should handle randomized commitFrom and commitTo of proper length', async function () { + await fc.assert( + fc.asyncProperty( + fc.stringMatching(/^[0-9a-fA-F]{40}$/), + fc.stringMatching(/^[0-9a-fA-F]{40}$/), + async (from, to) => { + const action = new Action('id', 'push', 'POST', Date.now(), 'test/repo'); + action.proxyGitPath = __dirname; + action.repoName = 'temp-test-repo'; + action.commitFrom = from; + action.commitTo = to; + action.commitData = [ + { parent: '0000000000000000000000000000000000000000' } + ]; + + const result = await exec({}, action); + + expect(result.steps[0].error).to.be.true; + expect(result.steps[0].errorMessage).to.contain('Invalid revision range'); + } + ), + { numRuns: 10 } + ); + }); + }); }); From e6afe09ed0168487730050d23e70a1eb0f66353a Mon Sep 17 00:00:00 2001 From: Juan Escalada Date: Mon, 21 Jul 2025 15:24:38 +0900 Subject: [PATCH 8/8] test(fuzz): testCheckRepoInAuthList --- test/testCheckRepoInAuthList.test.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/testCheckRepoInAuthList.test.js b/test/testCheckRepoInAuthList.test.js index 19d161c12..dea640ba2 100644 --- a/test/testCheckRepoInAuthList.test.js +++ b/test/testCheckRepoInAuthList.test.js @@ -2,6 +2,7 @@ const chai = require('chai'); const actions = require('../src/proxy/actions/Action'); const processor = require('../src/proxy/processors/push-action/checkRepoInAuthorisedList'); const expect = chai.expect; +const fc = require('fast-check'); const authList = () => { return [ @@ -24,4 +25,20 @@ describe('Check a Repo is in the authorised list', async () => { const result = await processor.exec(null, action, authList); expect(result.error).to.be.true; }); + + describe('fuzzing', () => { + it('should not crash on random repo names', async () => { + await fc.assert( + fc.asyncProperty( + fc.string(), + async (repoName) => { + const action = new actions.Action('123', 'type', 'get', 1234, repoName); + const result = await processor.exec(null, action, authList); + expect(result.error).to.be.true; + } + ), + { numRuns: 100 } + ); + }); + }); });