fix: close APT adversarial runtime gaps

This commit is contained in:
2026-08-19 21:59:19 +00:00
parent 2f492b662d
commit cf75ea9bc2
9 changed files with 688 additions and 233 deletions
+34
View File
@@ -2,6 +2,7 @@
const assert = require('node:assert/strict');
const test = require('node:test');
const { EventEmitter } = require('node:events');
const {
LATEST_RELEASE_URL,
@@ -9,6 +10,7 @@ const {
MAX_BODY_BYTES,
checkForUpdate,
compareSemver,
defaultRequest,
} = require('../src/update-policy');
function response(body, overrides = {}) {
@@ -188,3 +190,35 @@ test('unexpected HTTP and content types fail closed', async () => {
'INVALID_CONTENT_TYPE',
);
});
test('default transport enforces an absolute deadline despite trickled response bytes', async () => {
const request = new EventEmitter();
const responseStream = new EventEmitter();
request.destroyedWith = null;
responseStream.destroyed = false;
request.destroy = (error) => { request.destroyedWith = error; request.emit('error', error); };
request.setTimeout = () => { throw new Error('inactivity timeout must not be used'); };
responseStream.destroy = () => { responseStream.destroyed = true; };
responseStream.headers = { 'content-type': 'application/json' };
responseStream.statusCode = 200;
let deadline;
let cleared = false;
const pending = defaultRequest({
url: LATEST_RELEASE_URL,
timeoutMs: 5000,
maxBodyBytes: MAX_BODY_BYTES,
httpsGet: (_url, _options, onResponse) => {
onResponse(responseStream);
return request;
},
setTimer: (callback, milliseconds) => { assert.equal(milliseconds, 5000); deadline = callback; return 7; },
clearTimer: (timer) => { assert.equal(timer, 7); cleared = true; },
});
responseStream.emit('data', Buffer.from('{'));
responseStream.emit('data', Buffer.from(' '));
deadline();
await rejectsWithCode(pending, 'REQUEST_TIMEOUT');
assert.equal(responseStream.destroyed, true);
assert.equal(request.destroyedWith.code, 'REQUEST_TIMEOUT');
assert.equal(cleared, true);
});