/* eslint-env mocha */ import { expect } from 'chai'; import sinon from 'sinon'; import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; describe('attachmentApi authentication', function() { let findOneStub, hashStub; beforeEach(function() { hashStub = sinon.stub(Accounts, '_hashLoginToken'); findOneStub = sinon.stub(Meteor.users, 'findOne'); }); afterEach(function() { if (hashStub) hashStub.restore(); if (findOneStub) findOneStub.restore(); }); // Mock request/response objects function createMockReq(headers = {}) { return { headers, on: sinon.stub(), connection: { destroy: sinon.stub() }, }; } function createMockRes() { return { writeHead: sinon.stub(), end: sinon.stub(), headersSent: false, }; } describe('authenticateApiRequest', function() { it('denies request with missing X-User-Id header', function() { const req = createMockReq({ 'x-auth-token': 'sometoken' }); const res = createMockRes(); // Simulate the handler behavior let errorThrown = false; try { if (!req.headers['x-user-id'] || !req.headers['x-auth-token']) { throw new Meteor.Error('unauthorized', 'Missing X-User-Id or X-Auth-Token headers'); } } catch (error) { errorThrown = true; expect(error.error).to.equal('unauthorized'); } expect(errorThrown).to.equal(true); }); it('denies request with missing X-Auth-Token header', function() { const req = createMockReq({ 'x-user-id': 'user123' }); let errorThrown = false; try { if (!req.headers['x-user-id'] || !req.headers['x-auth-token']) { throw new Meteor.Error('unauthorized', 'Missing X-User-Id or X-Auth-Token headers'); } } catch (error) { errorThrown = true; expect(error.error).to.equal('unauthorized'); } expect(errorThrown).to.equal(true); }); it('denies request with invalid token', function() { const userId = 'user123'; const token = 'invalidtoken'; const req = createMockReq({ 'x-user-id': userId, 'x-auth-token': token }); hashStub.returns('hashedInvalidToken'); findOneStub.returns(null); // No user found let errorThrown = false; try { const hashedToken = Accounts._hashLoginToken(token); const user = Meteor.users.findOne({ _id: userId, 'services.resume.loginTokens.hashedToken': hashedToken, }); if (!user) { throw new Meteor.Error('unauthorized', 'Invalid credentials'); } } catch (error) { errorThrown = true; expect(error.error).to.equal('unauthorized'); } expect(errorThrown).to.equal(true); expect(hashStub.calledOnce).to.equal(true); expect(findOneStub.calledOnce).to.equal(true); }); it('allows request with valid X-User-Id and X-Auth-Token', function() { const userId = 'user123'; const token = 'validtoken'; const req = createMockReq({ 'x-user-id': userId, 'x-auth-token': token }); const hashedToken = 'hashedValidToken'; hashStub.returns(hashedToken); findOneStub.returns({ _id: userId }); // User found let authenticatedUserId = null; try { const hashed = Accounts._hashLoginToken(token); const user = Meteor.users.findOne({ _id: userId, 'services.resume.loginTokens.hashedToken': hashed, }); if (!user) { throw new Meteor.Error('unauthorized', 'Invalid credentials'); } authenticatedUserId = userId; } catch (error) { // Should not throw } expect(authenticatedUserId).to.equal(userId); expect(hashStub.calledOnce).to.equal(true); expect(hashStub.calledWith(token)).to.equal(true); expect(findOneStub.calledOnce).to.equal(true); const queryArg = findOneStub.getCall(0).args[0]; expect(queryArg._id).to.equal(userId); expect(queryArg['services.resume.loginTokens.hashedToken']).to.equal(hashedToken); }); it('prevents identity spoofing by validating hashed token', function() { const victimId = 'victim-user-id'; const attackerToken = 'attacker-token'; const req = createMockReq({ 'x-user-id': victimId, 'x-auth-token': attackerToken }); hashStub.returns('hashedAttackerToken'); // Simulate victim exists but token doesn't match findOneStub.returns(null); let errorThrown = false; try { const hashed = Accounts._hashLoginToken(attackerToken); const user = Meteor.users.findOne({ _id: victimId, 'services.resume.loginTokens.hashedToken': hashed, }); if (!user) { throw new Meteor.Error('unauthorized', 'Invalid credentials'); } } catch (error) { errorThrown = true; expect(error.error).to.equal('unauthorized'); } expect(errorThrown).to.equal(true); }); }); describe('request handler DoS prevention', function() { it('enforces timeout on hanging requests', function(done) { this.timeout(5000); const req = createMockReq({ 'x-user-id': 'user1', 'x-auth-token': 'token1' }); const res = createMockRes(); // Simulate timeout behavior const timeout = setTimeout(() => { if (!res.headersSent) { res.headersSent = true; res.writeHead(408, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: 'Request timeout' })); } }, 100); // Short timeout for test // Wait for timeout setTimeout(() => { expect(res.headersSent).to.equal(true); expect(res.writeHead.calledWith(408)).to.equal(true); clearTimeout(timeout); done(); }, 150); }); it('limits request body size', function() { const req = createMockReq({ 'x-user-id': 'user1', 'x-auth-token': 'token1' }); let body = ''; const limit = 50 * 1024 * 1024; // 50MB // Simulate exceeding limit body = 'a'.repeat(limit + 1); expect(body.length).to.be.greaterThan(limit); // Handler should destroy connection if (body.length > limit) { req.connection.destroy(); } expect(req.connection.destroy.calledOnce).to.equal(true); }); }); });