mirror of
https://github.com/saltbo/zpan.git
synced 2026-09-21 13:20:33 +08:00
fix: reject invalid cloud refresh certificates
This commit is contained in:
@@ -159,4 +159,37 @@ describe('performRefresh', () => {
|
||||
expect(state.refreshToken).toBe('old-rt')
|
||||
expect(state.lastRefreshError).toBe('Connection timeout')
|
||||
})
|
||||
|
||||
it('keeps the previous binding when cloud returns an invalid certificate', async () => {
|
||||
const db = makeDb()
|
||||
await seedBinding(db, {
|
||||
[LICENSE_KEYS.cachedCert]: 'old-cert',
|
||||
[LICENSE_KEYS.cachedExpiresAt]: '1234567890',
|
||||
})
|
||||
|
||||
const cert = sign(TEST_SECRET, {
|
||||
account_id: 'acct-1',
|
||||
instance_id: 'wrong-instance',
|
||||
plan: 'pro',
|
||||
features: ['white_label'],
|
||||
issued_at: new Date().toISOString(),
|
||||
expires_at: futureIso(3_600_000),
|
||||
})
|
||||
|
||||
vi.mocked(fetch).mockResolvedValueOnce({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => ({ refresh_token: 'new-rt', certificate: cert }),
|
||||
text: async () => '',
|
||||
} as unknown as Response)
|
||||
|
||||
await performRefresh(db, 'https://cloud.zpan.space')
|
||||
|
||||
const { loadLicenseState } = await import('./license-state')
|
||||
const state = await loadLicenseState(db)
|
||||
expect(state.refreshToken).toBe('old-rt')
|
||||
expect(state.cachedCert).toBe('old-cert')
|
||||
expect(state.cachedExpiresAt).toBe(1234567890)
|
||||
expect(state.lastRefreshError).toBe('Invalid certificate from cloud')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,6 +5,8 @@ import { invalidateEntitlementCache } from './entitlement'
|
||||
import { clearLicenseBinding, LICENSE_KEYS, loadLicenseState, setLicenseOptions } from './license-state'
|
||||
import { verifyCertificate } from './verify'
|
||||
|
||||
const INVALID_CERTIFICATE_ERROR = 'Invalid certificate from cloud'
|
||||
|
||||
function normaliseCert(raw: string, instanceId: string): { cert: string; entitlement: LicenseEntitlement | null } {
|
||||
const entitlement = verifyCertificate(raw, instanceId)
|
||||
return { cert: raw, entitlement }
|
||||
@@ -17,13 +19,17 @@ export async function performRefresh(db: Database, baseUrl: string): Promise<voi
|
||||
try {
|
||||
const data = await refreshEntitlement(baseUrl, state.refreshToken)
|
||||
const { cert, entitlement } = normaliseCert(data.certificate, state.instanceId)
|
||||
if (!entitlement) {
|
||||
await setLicenseOptions(db, {
|
||||
[LICENSE_KEYS.lastRefreshError]: INVALID_CERTIFICATE_ERROR,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
await setLicenseOptions(db, {
|
||||
[LICENSE_KEYS.refreshToken]: data.refresh_token,
|
||||
[LICENSE_KEYS.cachedCert]: cert,
|
||||
[LICENSE_KEYS.cachedExpiresAt]: entitlement
|
||||
? String(Math.floor(new Date(entitlement.expires_at).getTime() / 1000))
|
||||
: null,
|
||||
[LICENSE_KEYS.cachedExpiresAt]: String(Math.floor(new Date(entitlement.expires_at).getTime() / 1000)),
|
||||
[LICENSE_KEYS.lastRefreshAt]: String(Math.floor(Date.now() / 1000)),
|
||||
[LICENSE_KEYS.lastRefreshError]: null,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user