[grade=A] Harden server-managed license renewals
CI / Test & Lint (push) Canceled after 0s
CI / Security audit (push) Canceled after 0s

This commit is contained in:
Hermes
2026-08-22 18:15:37 -07:00
parent df55677bd1
commit 5e60c27f2b
5 changed files with 953 additions and 32 deletions
@@ -146,7 +146,7 @@ describe('LicenseManager: load()', () => {
} finally { await restore(); }
});
test('logs expired license on load but keeps it', async () => {
test('fails closed and removes expired license on load', async () => {
const { mgr, restore } = _makeManager();
try {
const activation = {
@@ -162,7 +162,7 @@ describe('LicenseManager: load()', () => {
await mgr.credentialManager.store('license.activation', JSON.stringify(activation));
await mgr.load();
expect(mgr.activation).toBeTruthy();
expect(mgr.activation).toBeNull();
expect(mgr.isExpired()).toBe(true);
expect(mgr._loaded).toBe(true);
} finally { await restore(); }
@@ -512,7 +512,7 @@ describe('LicenseManager: activate() — online validation', () => {
}
});
test('falls back to offline when server is unreachable (fetch throws)', async () => {
test('does not mint a new server-managed activation offline when server is unreachable', async () => {
const originalFetch = global.fetch;
const dir = _tmpDir();
const prevUrl = process.env.LICENSE_SERVER_URL;
@@ -540,8 +540,8 @@ describe('LicenseManager: activate() — online validation', () => {
});
const res = await result;
expect(res.success).toBe(true);
expect(res.activation.validationMethod).toBe('offline');
expect(res.success).toBe(false);
expect(res.message).toMatch(/temporarily unavailable/);
} finally {
if (prevUrl === undefined) delete process.env.LICENSE_SERVER_URL;
else process.env.LICENSE_SERVER_URL = prevUrl;
@@ -915,19 +915,19 @@ describe('LicenseManager: isExpired()', () => {
} finally { restore(); }
});
test('false for lifetime flag only (no durationDays)', () => {
test('fails closed for lifetime flag without signed zero duration', () => {
const { mgr, restore } = _makeManager();
try {
mgr.activation = { lifetime: true, expiresAt: '2020-01-01T00:00:00Z' };
expect(mgr.isExpired()).toBe(false);
expect(mgr.isExpired()).toBe(true);
} finally { restore(); }
});
test('false when expiresAt is null/missing (treated as lifetime)', () => {
test('fails closed when expiresAt is null or missing', () => {
const { mgr, restore } = _makeManager();
try {
mgr.activation = { durationDays: 30, lifetime: false, expiresAt: null };
expect(mgr.isExpired()).toBe(false);
expect(mgr.isExpired()).toBe(true);
} finally { restore(); }
});
@@ -1487,4 +1487,18 @@ describe('LicenseManager: full lifecycle integration', () => {
expect(result.activation.expired).toBe(false);
} finally { await restore(); }
});
test('expired offline code cannot mint a fresh entitlement term', async () => {
const actualNow = Date.now();
const nowSpy = jest.spyOn(Date, 'now').mockReturnValue(actualNow - 400 * 86400000);
const expiredCode = generateCode(TEST_SECRET, 30, 99123);
nowSpy.mockRestore();
const { mgr, restore } = _makeManager({ env: { LICENSE_SERVER_URL: undefined }, secret: TEST_SECRET });
try {
const result = await mgr.activate(expiredCode);
expect(result.success).toBe(false);
expect(result.message).toMatch(/expired/i);
expect(mgr.activation).toBeNull();
} finally { await restore(); }
});
});