wulf-pulse/lib/services/b2/client.test.ts
lorentz 1112a06afe feat: RMM Overshell, IT Glue audit/write-back, LogLift, link-aware bundles, dashboard overhaul
- RMM Overshell (migration 077): admin page, dispatch UI, executor/worker, target
  resolver, script registry (AD/DHCP/DNS/event-log/services/software/network/loglift)
- LogLift evidence pipeline (migration 078): upload webhook, B2 storage client,
  receiver/matcher, EventLogCollector PowerShell script
- IT Glue audit + write-back (migrations 075, 076): asset-audit runner, ticket
  xrefs, applications/configurations browse pages + apply/revert/audit endpoints
- Link-aware analyzer bundles (migration 073) + provider toggle (migration 074):
  link-discovery service, OpenRouter LLM provider, related-tickets/itglue-suggestion
  panels, analyze-bundle endpoint
- Endpoint data model + device-link reconciliation (migrations 079, 080): conflicts
  admin page, reconciler service, resolve endpoints
- Dashboard overhaul: integration-health service + alerts, overview/health endpoints
- Permissions: add itglue + rmm scopes; middleware: public /api/rmm/loglift route

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 07:13:18 -04:00

130 lines
3.9 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import {
OBJECT_KEY_REGEX,
presignDownload,
presignUpload,
B2InvalidObjectKeyError,
type B2Config,
_B2_INTERNALS,
} from './client';
const FIXTURE_CFG: B2Config = {
keyId: 'AKIA-FIXTURE',
secret: 'sec-fixture',
bucket: 'wulf-audits',
region: 'us-west-002',
endpoint: 's3.us-west-002.backblazeb2.com',
};
describe('OBJECT_KEY_REGEX', () => {
it('accepts the production shape', () => {
expect(
OBJECT_KEY_REGEX.test(
'ba03268b-5528-4dde-ad76-867523446ecd/unknown-server/eventlogs_20251202_173301.json.gz'
)
).toBe(true);
expect(
OBJECT_KEY_REGEX.test(
'site_uuid_short/MISYS-SQL/eventlogs_20260502_120000.json.gz'
)
).toBe(true);
});
it('rejects path traversal', () => {
expect(OBJECT_KEY_REGEX.test('../etc/passwd')).toBe(false);
expect(OBJECT_KEY_REGEX.test('site/../../escape/eventlogs_1.json.gz')).toBe(false);
});
it('rejects wrong shapes', () => {
expect(OBJECT_KEY_REGEX.test('site/host/something.json.gz')).toBe(false); // missing eventlogs_ prefix
expect(OBJECT_KEY_REGEX.test('eventlogs_1.json.gz')).toBe(false); // missing prefix dirs
expect(OBJECT_KEY_REGEX.test('site/host/eventlogs_1.json')).toBe(false); // missing .gz
expect(OBJECT_KEY_REGEX.test('site host/x/eventlogs_1.json.gz')).toBe(false); // space in client id
});
});
describe('presignDownload + presignUpload', () => {
const realDate = Date;
beforeEach(() => {
// Pin time so signatures are deterministic.
const fixed = new Date('2026-05-02T20:00:00.000Z');
vi.stubGlobal(
'Date',
class extends realDate {
constructor(...args: unknown[]) {
if (args.length === 0) {
super(fixed.getTime());
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
super(...(args as [any]));
}
}
static now() {
return fixed.getTime();
}
} as unknown as DateConstructor
);
});
afterEach(() => {
vi.unstubAllGlobals();
});
it('produces a stable presigned GET URL', () => {
const url = presignDownload(
'site/host/eventlogs_20260502_120000.json.gz',
600,
FIXTURE_CFG
);
expect(url).toContain('https://s3.us-west-002.backblazeb2.com/wulf-audits/');
expect(url).toContain('X-Amz-Algorithm=AWS4-HMAC-SHA256');
expect(url).toContain('X-Amz-Credential=AKIA-FIXTURE');
expect(url).toContain('X-Amz-Date=20260502T200000Z');
expect(url).toContain('X-Amz-Expires=600');
expect(url).toContain('X-Amz-SignedHeaders=host');
expect(url).toMatch(/X-Amz-Signature=[a-f0-9]{64}$/);
});
it('produces a presigned PUT URL with PUT method scope', () => {
const url = presignUpload(
'site/host/eventlogs_20260502_120000.json.gz',
1800,
FIXTURE_CFG
);
expect(url).toContain('X-Amz-Expires=1800');
expect(url).toMatch(/X-Amz-Signature=[a-f0-9]{64}$/);
});
it('rejects path-traversal object keys', () => {
expect(() =>
presignDownload('../etc/eventlogs_1.json.gz', 600, FIXTURE_CFG)
).toThrow(B2InvalidObjectKeyError);
});
it('different methods produce different signatures (sanity check)', () => {
const get = presignDownload(
'site/host/eventlogs_20260502_120000.json.gz',
600,
FIXTURE_CFG
);
const put = presignUpload(
'site/host/eventlogs_20260502_120000.json.gz',
600,
FIXTURE_CFG
);
const sigGet = get.split('X-Amz-Signature=')[1];
const sigPut = put.split('X-Amz-Signature=')[1];
expect(sigGet).not.toBe(sigPut);
});
});
describe('deriveSigningKey', () => {
it('produces a 32-byte HMAC-SHA256 chain', () => {
const k = _B2_INTERNALS.deriveSigningKey(
'sec-fixture',
'20260502',
'us-west-002',
's3'
);
expect(k.length).toBe(32);
});
});