feat(api-security): encrypt sensitive data at rest
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import request from "supertest";
|
||||
import { INestApplication, ValidationPipe } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import {
|
||||
AiChannel,
|
||||
@@ -19,6 +20,7 @@ import {
|
||||
AiRouteFailureError
|
||||
} from "../src/ai/ai.types";
|
||||
import { PrismaService } from "../src/prisma/prisma.service";
|
||||
import { DataEncryptionService } from "../src/security/data-encryption.service";
|
||||
|
||||
type AiUsageLogRecord = {
|
||||
id: string;
|
||||
@@ -297,6 +299,10 @@ class InMemoryAiPrismaService {
|
||||
return [...this.usageLogs];
|
||||
}
|
||||
|
||||
getBindings(): AiProviderBinding[] {
|
||||
return [...this.bindings];
|
||||
}
|
||||
|
||||
seedTask(task: AiTaskRecord): void {
|
||||
this.tasks.push(task);
|
||||
}
|
||||
@@ -401,10 +407,18 @@ describe("AiController (integration)", () => {
|
||||
controllers: [AiController],
|
||||
providers: [
|
||||
AiService,
|
||||
DataEncryptionService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaService
|
||||
},
|
||||
{
|
||||
provide: ConfigService,
|
||||
useValue: {
|
||||
get: (key: string) =>
|
||||
key === "DATA_ENCRYPTION_SECRET" ? "test-data-encryption-secret" : undefined
|
||||
}
|
||||
},
|
||||
{
|
||||
provide: AiProviderRegistryService,
|
||||
useValue: {
|
||||
@@ -466,6 +480,11 @@ describe("AiController (integration)", () => {
|
||||
maskedApiKey: "abk_***34",
|
||||
isEnabled: true
|
||||
});
|
||||
|
||||
const storedBinding = prismaService.getBindings()[0];
|
||||
expect(storedBinding?.providerName).not.toBe("astrbot-main");
|
||||
expect(storedBinding?.endpoint).not.toBe("http://127.0.0.1:6185");
|
||||
expect(storedBinding?.encryptedApiKey).not.toBe("abk_secret_1234");
|
||||
});
|
||||
|
||||
it("should hide public pool endpoint from user bindings response", async () => {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import request from "supertest";
|
||||
import { INestApplication, ValidationPipe } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { PrismaService } from "../src/prisma/prisma.service";
|
||||
import { DataEncryptionService } from "../src/security/data-encryption.service";
|
||||
import { SyncController } from "../src/sync/sync.controller";
|
||||
import { SyncService } from "../src/sync/sync.service";
|
||||
|
||||
@@ -159,6 +161,10 @@ class InMemoryPrismaService {
|
||||
return this.syncOperations.length;
|
||||
}
|
||||
|
||||
getRawOperationById(opId: string): SyncOperationRecord | undefined {
|
||||
return this.syncOperations.find((operation) => operation.opId === opId);
|
||||
}
|
||||
|
||||
seedOperations(records: Array<Omit<SyncOperationRecord, "id">>): void {
|
||||
for (const record of records) {
|
||||
this.syncOperations.push({
|
||||
@@ -196,7 +202,18 @@ describe("SyncController (integration)", () => {
|
||||
|
||||
const moduleRef: TestingModule = await Test.createTestingModule({
|
||||
controllers: [SyncController],
|
||||
providers: [SyncService, { provide: PrismaService, useValue: prismaService }]
|
||||
providers: [
|
||||
SyncService,
|
||||
DataEncryptionService,
|
||||
{ provide: PrismaService, useValue: prismaService },
|
||||
{
|
||||
provide: ConfigService,
|
||||
useValue: {
|
||||
get: (key: string) =>
|
||||
key === "DATA_ENCRYPTION_SECRET" ? "test-data-encryption-secret" : undefined
|
||||
}
|
||||
}
|
||||
]
|
||||
}).compile();
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
@@ -258,6 +275,9 @@ describe("SyncController (integration)", () => {
|
||||
})
|
||||
]);
|
||||
expect(prismaService.getOperationCount()).toBe(2);
|
||||
expect(prismaService.getRawOperationById("op-create-1")?.payload).not.toBe(
|
||||
'{"title":"浠诲姟涓€"}'
|
||||
);
|
||||
|
||||
const secondResponse = await request(app.getHttpServer())
|
||||
.post("/sync/push")
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import request from "supertest";
|
||||
import { INestApplication, ValidationPipe } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { PrismaService } from "../src/prisma/prisma.service";
|
||||
import { DataEncryptionService } from "../src/security/data-encryption.service";
|
||||
import { TaskController } from "../src/task/task.controller";
|
||||
import { TaskService } from "../src/task/task.service";
|
||||
import { TaskPriority, TaskStatus } from "../generated/prisma/client";
|
||||
@@ -355,6 +357,10 @@ class InMemoryPrismaService {
|
||||
return runner(this);
|
||||
}
|
||||
|
||||
getRawTaskById(taskId: string): TaskRecord | undefined {
|
||||
return this.tasks.find((task) => task.id === taskId);
|
||||
}
|
||||
|
||||
private toTaskWithTags(
|
||||
task: TaskRecord
|
||||
): TaskRecord & { taskTags: Array<{ tag: { name: string } }> } {
|
||||
@@ -390,7 +396,15 @@ describe("TaskController (integration)", () => {
|
||||
controllers: [TaskController],
|
||||
providers: [
|
||||
TaskService,
|
||||
{ provide: PrismaService, useValue: prismaService as unknown as PrismaService }
|
||||
DataEncryptionService,
|
||||
{ provide: PrismaService, useValue: prismaService as unknown as PrismaService },
|
||||
{
|
||||
provide: ConfigService,
|
||||
useValue: {
|
||||
get: (key: string) =>
|
||||
key === "DATA_ENCRYPTION_SECRET" ? "test-data-encryption-secret" : undefined
|
||||
}
|
||||
}
|
||||
]
|
||||
}).compile();
|
||||
|
||||
@@ -425,6 +439,9 @@ describe("TaskController (integration)", () => {
|
||||
expect(createResponse.body.id).toBeDefined();
|
||||
expect(createResponse.body.tags).toEqual(["工作", "会议"]);
|
||||
const taskId = createResponse.body.id as string;
|
||||
const rawCreatedTask = prismaService.getRawTaskById(taskId);
|
||||
expect(rawCreatedTask?.title).not.toBe("准备周会");
|
||||
expect(rawCreatedTask?.contentText).not.toBe("整理本周进度");
|
||||
|
||||
const listResponse = await request(app.getHttpServer())
|
||||
.get("/tasks")
|
||||
|
||||
Reference in New Issue
Block a user