Track HiveGrid Cloud planning reference
This commit is contained in:
1 parent
b3d5ea6764
commit
e341c5938a
5784 files changed
+1104929
-3
No files matched your search
@@ -0,0 +1,108 @@
|
||||
// @vitest-environment node
|
||||
import { describe, expect, test } from "vitest";
|
||||
import type { ApiKeyWithDetails } from "./apiKeys.db.server";
|
||||
import { getApiKeyLookupCacheKey, serializeApiLogSearchParams } from "../api/apiKeySecurity.server";
|
||||
import { redactApiKeyForDisplay, redactApiKeysForDisplay } from "./apiKeys.db.server";
|
||||
|
||||
const RAW_SECRET = "raw-secret-value-that-must-not-leak";
|
||||
|
||||
function createApiKeyRow(overrides: Partial<ApiKeyWithDetails> = {}): ApiKeyWithDetails {
|
||||
const now = new Date("2026-01-02T03:04:05.000Z");
|
||||
|
||||
return {
|
||||
id: "api-key-1",
|
||||
tenantId: "tenant-1",
|
||||
createdByUserId: "user-1",
|
||||
alias: "Primary integration",
|
||||
key: RAW_SECRET,
|
||||
keyHash: "hash-value",
|
||||
keyPrefix: "raw-secr",
|
||||
keyLastFour: "leak",
|
||||
hashedAt: now,
|
||||
keyVersion: 1,
|
||||
expires: null,
|
||||
active: true,
|
||||
createdAt: now,
|
||||
tenant: { id: "tenant-1", name: "Tenant", slug: "tenant", deactivatedReason: null },
|
||||
createdByUser: {
|
||||
id: "user-1",
|
||||
email: "user@example.com",
|
||||
firstName: "Ada",
|
||||
lastName: "Lovelace",
|
||||
phone: null,
|
||||
locale: "en",
|
||||
createdAt: now,
|
||||
},
|
||||
entities: [
|
||||
{
|
||||
id: "permission-1",
|
||||
apiKeyId: "api-key-1",
|
||||
entityId: "entity-1",
|
||||
create: true,
|
||||
read: true,
|
||||
update: false,
|
||||
delete: false,
|
||||
entity: { id: "entity-1", title: "Contact", titlePlural: "Contacts", slug: "contacts" },
|
||||
},
|
||||
],
|
||||
_count: { apiKeyLogs: 7 },
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("API key display redaction", () => {
|
||||
test("omits raw key while preserving display metadata for detail payloads", () => {
|
||||
const source = createApiKeyRow();
|
||||
|
||||
const redacted = redactApiKeyForDisplay(source);
|
||||
|
||||
expect(redacted).not.toHaveProperty("key");
|
||||
expect(redacted).toMatchObject({
|
||||
id: source.id,
|
||||
alias: source.alias,
|
||||
active: source.active,
|
||||
expires: source.expires,
|
||||
keyPrefix: source.keyPrefix,
|
||||
keyLastFour: source.keyLastFour,
|
||||
hashedAt: source.hashedAt,
|
||||
keyVersion: source.keyVersion,
|
||||
entities: source.entities,
|
||||
createdByUser: source.createdByUser,
|
||||
tenant: source.tenant,
|
||||
_count: source._count,
|
||||
});
|
||||
expect(JSON.stringify(redacted)).not.toContain(RAW_SECRET);
|
||||
});
|
||||
|
||||
test("redacts list payloads without mutating source rows", () => {
|
||||
const source = createApiKeyRow();
|
||||
|
||||
const redacted = redactApiKeysForDisplay([source]);
|
||||
|
||||
expect(redacted).toHaveLength(1);
|
||||
expect(redacted[0]).not.toHaveProperty("key");
|
||||
expect(JSON.stringify(redacted)).not.toContain(RAW_SECRET);
|
||||
expect(source.key).toBe(RAW_SECRET);
|
||||
expect(source).toHaveProperty("key", RAW_SECRET);
|
||||
});
|
||||
|
||||
test("builds API-key cache keys without embedding raw secrets", () => {
|
||||
const cacheKey = getApiKeyLookupCacheKey(RAW_SECRET);
|
||||
|
||||
expect(cacheKey).toMatch(/^apiKeyHash:[a-f0-9]{64}$/);
|
||||
expect(cacheKey).not.toContain(RAW_SECRET);
|
||||
});
|
||||
|
||||
test("redacts query params for API-key log writers", () => {
|
||||
const request = new Request("https://example.com/api/site?token=raw-token&email=person@example.test&page=2&apiKey=sk_live_secret");
|
||||
|
||||
expect(serializeApiLogSearchParams(request)).toBe(
|
||||
JSON.stringify({
|
||||
token: "[REDACTED]",
|
||||
email: "[REDACTED]",
|
||||
page: "2",
|
||||
apiKey: "[REDACTED]",
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user