-
-
Notifications
You must be signed in to change notification settings - Fork 18
secret storage #1447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
secret storage #1447
Conversation
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements a secret storage feature that allows users to securely store, manage, and audit sensitive company information like API keys, tokens, and passwords. The feature includes encrypted storage with optional master password protection, comprehensive audit logging, and company-based access control.
- Adds complete secret storage backend with entities, service, controller, and DTOs
- Implements two-layer encryption (application-level + optional master password)
- Adds database migrations for user secrets and audit logs
- Includes comprehensive unit and E2E test coverage
- Updates Docker configuration for test environment
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 26 comments.
Show a summary per file
| File | Description |
|---|---|
| backend/test/ava-tests/non-saas-tests/non-saas-secrets-e2e.test.ts | Comprehensive E2E tests covering all secret operations including CRUD, authentication, authorization, and master password protection |
| backend/src/migrations/1763724062000-CreateSecretAccessLogEntity.ts | Database migration for audit log table with proper indexes and foreign keys |
| backend/src/migrations/1763724061000-CreateUserSecretEntity.ts | Database migration for user secrets table with encryption support and company-level scoping |
| backend/src/entities/user-secret/user-secrets.service.ts | Core service implementing secret management with encryption, access control, and audit logging |
| backend/src/entities/user-secret/user-secrets.service.spec.ts | Unit tests for the secrets service covering all major operations and error cases |
| backend/src/entities/user-secret/user-secret.module.ts | NestJS module definition registering the secret storage components |
| backend/src/entities/user-secret/user-secret.entity.ts | TypeORM entity for user secrets with relationships and indexes |
| backend/src/entities/user-secret/user-secret.controller.ts | REST API controller exposing secret management endpoints |
| backend/src/entities/user-secret/application/dto/update-secret.dto.ts | DTO for updating secret values with validation |
| backend/src/entities/user-secret/application/dto/secret-list.dto.ts | DTOs for paginated secret list responses |
| backend/src/entities/user-secret/application/dto/found-secret.dto.ts | DTO for returning secret details with optional value |
| backend/src/entities/user-secret/application/dto/create-secret.dto.ts | DTO for creating secrets with validation and master password support |
| backend/src/entities/user-secret/application/dto/audit-log.dto.ts | DTOs for audit log entries and paginated responses |
| backend/src/entities/secret-access-log/secret-access-log.entity.ts | TypeORM entity for tracking all secret access events |
| backend/src/decorators/company-id.decorator.ts | Custom decorator for extracting company ID from authenticated requests |
| backend/src/app.module.ts | Registers the UserSecretModule in the main application module |
| justfile | Adds --attach flag to focus test output on backend_test container |
| docker-compose.tst.yml | Updates test environment with explicit DATABASE_URL and simplified volume mounts |
| Dockerfile | Modifies file ownership configuration for the application directory |
| SECRET_STORAGE_SPECIFICATION.md | Comprehensive specification document detailing the secret storage feature design |
Comments suppressed due to low confidence (1)
backend/test/ava-tests/non-saas-tests/non-saas-secrets-e2e.test.ts:71
- Unused variable connectionId.
const connectionId = JSON.parse(createdConnection.text).id;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| .set('Content-Type', 'application/json') | ||
| .set('Accept', 'application/json'); | ||
|
|
||
| const connectionId = JSON.parse(createdConnection.text).id; |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable connectionId is created but never used in this test or any other tests in the file. Each test creates a connection but doesn't use the connectionId for any assertions or operations. Consider removing these unused variable declarations throughout the test file (lines 71, 107, 138, 170, 202, 228, 254, 277, 312, 334, 361, 392, 426, 455, 490, 516, 550, 573, 600, 623) to improve code maintainability.
| const connectionId = JSON.parse(createdConnection.text).id; | |
| // Removed unused connectionId variable |
backend/src/entities/user-secret/application/dto/update-secret.dto.ts
Outdated
Show resolved
Hide resolved
| Post, | ||
| Put, | ||
| Query, | ||
| UseGuards, |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import UseGuards.
| UseGuards, |
| jest.spyOn(auditLogRepository, 'save').mockResolvedValue({} as SecretAccessLogEntity); | ||
|
|
||
| const result = await service.updateSecret('user-uuid-1', 'test-secret', updateDto); | ||
|
|
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable result.
| expect(result.slug).toBe('test-secret'); | |
| expect(result.encryptedValue).toBe('new-value'); |
| .set('Content-Type', 'application/json') | ||
| .set('Accept', 'application/json'); | ||
|
|
||
| const connectionId = JSON.parse(createdConnection.text).id; |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable connectionId.
| const connectionId = JSON.parse(createdConnection.text).id; |
| .set('Content-Type', 'application/json') | ||
| .set('Accept', 'application/json'); | ||
|
|
||
| const connectionId = JSON.parse(createdConnection.text).id; |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable connectionId.
| .set('Content-Type', 'application/json') | ||
| .set('Accept', 'application/json'); | ||
|
|
||
| const connectionId = JSON.parse(createdConnection.text).id; |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable connectionId.
| .set('Content-Type', 'application/json') | ||
| .set('Accept', 'application/json'); | ||
|
|
||
| const connectionId = JSON.parse(createdConnection.text).id; |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable connectionId.
| .set('Content-Type', 'application/json') | ||
| .set('Accept', 'application/json'); | ||
|
|
||
| const connectionId = JSON.parse(createdConnection.text).id; |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable connectionId.
| const connectionId = JSON.parse(createdConnection.text).id; |
Artuomka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation does not align with the overall project architecture.
New entities have been added that are related to existing ones, but the corresponding changes were not made to the existing entities.
backend/src/entities/secret-access-log/secret-access-log.entity.ts
Outdated
Show resolved
Hide resolved
backend/src/entities/user-secret/application/dto/audit-log.dto.ts
Outdated
Show resolved
Hide resolved
|
|
||
| export class AuditLogResponseDto { | ||
| @ApiProperty({ type: [AuditLogEntryDto] }) | ||
| data: AuditLogEntryDto[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
({ type: AuditLogEntryDto, isArray: true })
backend/src/entities/secret-access-log/repository/secret-access-log-repository.extension.ts
Outdated
Show resolved
Hide resolved
backend/src/entities/secret-access-log/repository/secret-access-log-repository.extension.ts
Outdated
Show resolved
Hide resolved
backend/src/entities/secret-access-log/secret-access-log.entity.ts
Outdated
Show resolved
Hide resolved
backend/src/entities/user-secret/use-cases/delete-secret.use.case.ts
Outdated
Show resolved
Hide resolved
backend/src/entities/user-secret/use-cases/get-secret-audit-log.use.case.ts
Outdated
Show resolved
Hide resolved
backend/src/entities/user-secret/use-cases/get-secret-by-slug.use.case.ts
Outdated
Show resolved
Hide resolved
backend/src/entities/user-secret/use-cases/get-secrets.use.case.ts
Outdated
Show resolved
Hide resolved
backend/src/entities/user-secret/use-cases/update-secret.use.case.ts
Outdated
Show resolved
Hide resolved
| @Column({ type: 'varchar', length: 4096, nullable: true }) | ||
| masterHash: string; | ||
|
|
||
| @OneToMany(() => SecretAccessLogEntity, (log) => log.secret) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to add description of the inverse relation
| @PrimaryGeneratedColumn('uuid') | ||
| id: string; | ||
|
|
||
| @ManyToOne(() => CompanyInfoEntity, { onDelete: 'CASCADE' }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to add description of the inverse relation
Artuomka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to improve swagger DTOs, need to add inverse relations in TypeORM entities (secret.accessLogs ↔ accessLog.secret), change ForbiddenException to NotFoundException when user is not found, and add guard to endpoints, if its needed
No description provided.