mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 16:01:54 +00:00
fix(dashboard): return timingSafeEqual result instead of discarding it
The unequal-length branch of timingSafeCompare called crypto.timingSafeEqual(paddedA, paddedB) but did not capture the return value, unconditionally returning false and leaving the timing-safe comparison as dead code. Adds a regression test covering shorter, longer, and exact-match credential lengths to lock in the correct behaviour. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
a5f08ff082
commit
a8ee3d39ea
2 files changed
+43
-2
No files matched your search
+1
-2
@@ -26,8 +26,7 @@ function timingSafeCompare(a, b) {
|
||||
const paddedB = Buffer.alloc(maxLen);
|
||||
bufA.copy(paddedA);
|
||||
bufB.copy(paddedB);
|
||||
crypto.timingSafeEqual(paddedA, paddedB);
|
||||
return false;
|
||||
return crypto.timingSafeEqual(paddedA, paddedB);
|
||||
}
|
||||
return crypto.timingSafeEqual(bufA, bufB);
|
||||
}
|
||||
|
||||
@@ -88,6 +88,48 @@ test('Dashboard Authentication', async (t) => {
|
||||
}
|
||||
});
|
||||
|
||||
await t.test('returns 401 when password has different byte length than stored credential', async () => {
|
||||
// Regression test for timingSafeCompare: previously the comparison result
|
||||
// was discarded and `false` was returned unconditionally for unequal-length
|
||||
// inputs, making the timing-safe branch dead code. Verify that a prefix of
|
||||
// the correct password (shorter) and an extended version (longer) are both
|
||||
// rejected.
|
||||
process.env.TORCH_DASHBOARD_AUTH = 'admin:secret';
|
||||
_resetTorchConfigCache();
|
||||
|
||||
server = await cmdDashboard(0);
|
||||
testPort = server.address().port;
|
||||
|
||||
try {
|
||||
const makeRequest = (credentials) => {
|
||||
const auth = Buffer.from(credentials).toString('base64');
|
||||
return new Promise((resolve) => {
|
||||
http.get({
|
||||
host: '127.0.0.1',
|
||||
port: testPort,
|
||||
path: '/dashboard/',
|
||||
headers: { 'Authorization': `Basic ${auth}` }
|
||||
}, resolve);
|
||||
});
|
||||
};
|
||||
|
||||
// Shorter than correct password — different byte length
|
||||
const resShorter = await makeRequest('admin:secre');
|
||||
assert.strictEqual(resShorter.statusCode, 401, 'shorter password must be rejected');
|
||||
|
||||
// Longer than correct password — different byte length
|
||||
const resLonger = await makeRequest('admin:secret_extra');
|
||||
assert.strictEqual(resLonger.statusCode, 401, 'longer password must be rejected');
|
||||
|
||||
// Correct credentials still work
|
||||
const resCorrect = await makeRequest('admin:secret');
|
||||
assert.notStrictEqual(resCorrect.statusCode, 401, 'correct credentials must not be rejected');
|
||||
} finally {
|
||||
delete process.env.TORCH_DASHBOARD_AUTH;
|
||||
_resetTorchConfigCache();
|
||||
}
|
||||
});
|
||||
|
||||
await t.test('returns 200 when auth is disabled', async () => {
|
||||
delete process.env.TORCH_DASHBOARD_AUTH;
|
||||
_resetTorchConfigCache();
|
||||
|
||||
Reference in new issue
Block a user