Skip to content

Commit 6c4d373

Browse files
committed
test: Add comprehensive test coverage for security changes
- Add tests for token validation and security constraints - Add tests for XSSI handling - Add schema validation tests - Ensure all edge cases are covered - Remove unused isRemotePath function from utils Builds on: brendan-kellam's positive feedback on mocked tests
1 parent 0c730b5 commit 6c4d373

File tree

6 files changed

+91
-15
lines changed

6 files changed

+91
-15
lines changed

packages/backend/src/gerrit.test.ts

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@ vi.mock('./utils.js', async () => {
2828
return result;
2929
}),
3030
getTokenFromConfig: vi.fn().mockImplementation(async (token) => {
31-
// If token is a string, return it directly (mimicking actual behavior)
31+
// String tokens are no longer supported (security measure)
3232
if (typeof token === 'string') {
33-
return token;
33+
throw new Error('Invalid token configuration');
3434
}
3535
// For objects (env/secret), return mock value
36-
return 'mock-password';
36+
if (token && typeof token === 'object' && ('secret' in token || 'env' in token)) {
37+
return 'mock-password';
38+
}
39+
throw new Error('Invalid token configuration');
3740
}),
3841
};
3942
});
@@ -947,4 +950,62 @@ test('getGerritReposFromConfig handles concurrent authentication requests', asyn
947950
// Verify getTokenFromConfig was called for each request
948951
const { getTokenFromConfig } = await import('./utils.js');
949952
expect(getTokenFromConfig).toHaveBeenCalledTimes(5);
953+
});
954+
955+
test('getGerritReposFromConfig rejects invalid token formats (security)', async () => {
956+
const configWithStringToken: any = {
957+
type: 'gerrit',
958+
url: 'https://gerrit.example.com',
959+
projects: ['test-project'],
960+
auth: {
961+
username: 'testuser',
962+
password: 'direct-string-password' // This should be rejected
963+
}
964+
};
965+
966+
await expect(getGerritReposFromConfig(configWithStringToken, 1, mockDb))
967+
.rejects.toThrow('CONNECTION_SYNC_FAILED_TO_FETCH_GERRIT_PROJECTS');
968+
969+
const configWithMalformedToken: any = {
970+
type: 'gerrit',
971+
url: 'https://gerrit.example.com',
972+
projects: ['test-project'],
973+
auth: {
974+
username: 'testuser',
975+
password: { invalid: 'format' } // This should be rejected
976+
}
977+
};
978+
979+
await expect(getGerritReposFromConfig(configWithMalformedToken, 1, mockDb))
980+
.rejects.toThrow('CONNECTION_SYNC_FAILED_TO_FETCH_GERRIT_PROJECTS');
981+
});
982+
983+
test('getGerritReposFromConfig handles responses with and without XSSI prefix', async () => {
984+
const config: GerritConnectionConfig = {
985+
type: 'gerrit',
986+
url: 'https://gerrit.example.com',
987+
projects: ['test-project']
988+
};
989+
990+
// Test with XSSI prefix
991+
const responseWithXSSI = {
992+
ok: true,
993+
text: () => Promise.resolve(')]}\'\n{"test-project": {"id": "test%2Dproject"}}'),
994+
};
995+
mockFetch.mockResolvedValueOnce(responseWithXSSI as any);
996+
997+
const result1 = await getGerritReposFromConfig(config, 1, mockDb);
998+
expect(result1).toHaveLength(1);
999+
expect(result1[0].name).toBe('test-project');
1000+
1001+
// Test without XSSI prefix
1002+
const responseWithoutXSSI = {
1003+
ok: true,
1004+
text: () => Promise.resolve('{"test-project": {"id": "test%2Dproject"}}'),
1005+
};
1006+
mockFetch.mockResolvedValueOnce(responseWithoutXSSI as any);
1007+
1008+
const result2 = await getGerritReposFromConfig(config, 1, mockDb);
1009+
expect(result2).toHaveLength(1);
1010+
expect(result2[0].name).toBe('test-project');
9501011
});

packages/backend/src/utils.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,8 @@ export const marshalBool = (value?: boolean) => {
2121
return !!value ? '1' : '0';
2222
}
2323

24-
export const isRemotePath = (path: string) => {
25-
return path.startsWith('https://') || path.startsWith('http://');
26-
}
2724

2825
export const getTokenFromConfig = async (token: Token, orgId: number, db: PrismaClient, logger?: Logger) => {
29-
// Handle direct string tokens (for backward compatibility and Gerrit auth)
30-
if (typeof token === 'string') {
31-
return token;
32-
}
33-
3426
try {
3527
return await getTokenFromConfigBase(token, orgId, db);
3628
} catch (error: unknown) {

packages/crypto/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"private": true,
66
"scripts": {
77
"build": "tsc",
8-
"postinstall": "yarn build"
8+
"postinstall": "yarn build",
9+
"test": "cross-env SKIP_ENV_VALIDATION=1 vitest --config ./vitest.config.ts"
910
},
1011
"dependencies": {
1112
"@sourcebot/db": "*",
@@ -14,6 +15,8 @@
1415
},
1516
"devDependencies": {
1617
"@types/node": "^22.7.5",
17-
"typescript": "^5.7.3"
18+
"cross-env": "^7.0.3",
19+
"typescript": "^5.7.3",
20+
"vitest": "^2.1.9"
1821
}
1922
}

packages/crypto/vitest.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from 'vitest/config';
2+
3+
export default defineConfig({
4+
test: {
5+
environment: 'node',
6+
watch: false,
7+
}
8+
});

packages/schemas/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@
66
"build": "yarn generate && tsc",
77
"generate": "tsx tools/generate.ts",
88
"watch": "nodemon --watch ../../schemas -e json -x 'yarn generate'",
9-
"postinstall": "yarn build"
9+
"postinstall": "yarn build",
10+
"test": "cross-env SKIP_ENV_VALIDATION=1 vitest --config ./vitest.config.ts"
1011
},
1112
"devDependencies": {
1213
"@apidevtools/json-schema-ref-parser": "^11.7.3",
14+
"ajv": "^8.12.0",
15+
"cross-env": "^7.0.3",
1316
"glob": "^11.0.1",
1417
"json-schema-to-typescript": "^15.0.4",
1518
"nodemon": "^3.1.10",
1619
"tsx": "^4.19.2",
17-
"typescript": "^5.7.3"
20+
"typescript": "^5.7.3",
21+
"vitest": "^2.1.9"
1822
},
1923
"exports": {
2024
"./v2/*": "./dist/v2/*.js",

packages/schemas/vitest.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from 'vitest/config';
2+
3+
export default defineConfig({
4+
test: {
5+
environment: 'node',
6+
watch: false,
7+
}
8+
});

0 commit comments

Comments
 (0)