Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
148 commits
Select commit Hold shift + click to select a range
76d2539
feat(cloud-security): cloud tests v2 — services, remediation, and mul…
tofikwest Apr 9, 2026
d2a3a20
fix(cloud-security): address Bugbot review findings
tofikwest Apr 9, 2026
b76b69a
Merge branch 'main' into tofik/q1-cloud-tests-v2
tofikwest Apr 9, 2026
40d18f8
fix(cloud-security): address CodeQL URL sanitization and role escalation
tofikwest Apr 9, 2026
f1c0d1e
fix(cloud-security): address remaining review findings
tofikwest Apr 9, 2026
e3d477c
fix(auth): validate x-user-id header against organization membership
tofikwest Apr 9, 2026
8714be2
fix(cloud-security): address final Bugbot review findings
tofikwest Apr 9, 2026
9e6e7ec
fix(cloud-security): fix plan cache key, wildcard IAM, and async poll
tofikwest Apr 9, 2026
0f279a4
fix(cloud-security): scope check result queries by connection
tofikwest Apr 9, 2026
6a95756
fix(cloud-security): catch async poll exceptions in Azure executor
tofikwest Apr 9, 2026
218f386
fix(cloud-security): use composite plan cache key for AWS remediation
tofikwest Apr 9, 2026
3210003
fix(cloud-security): remove unused @UserId from scan, validate subscr…
tofikwest Apr 9, 2026
da842d3
fix(cloud-security): fix undeclared userId crash and ARM token for Graph
tofikwest Apr 9, 2026
93a7f80
fix(cloud-security): remove Azure self-healing role grant entirely
tofikwest Apr 9, 2026
7f0fc14
fix(cloud-security): handle malformed AI-generated URLs in GCP preview
tofikwest Apr 9, 2026
c4cefd5
fix(cloud-security): validate Azure fix plan URLs before execution
tofikwest Apr 10, 2026
0ce8f53
fix(cloud-security): guard against undefined rollback steps in Azure …
tofikwest Apr 10, 2026
5e69ea3
fix(cloud-security): return correct verification status in API response
tofikwest Apr 10, 2026
6cdf207
fix(cloud-security): handle non-JSON success responses in Azure executor
tofikwest Apr 10, 2026
103e052
fix(cloud-security): handle 'system' user ID in activity service
tofikwest Apr 10, 2026
8121b35
fix(cloud-security): validate all step URLs in executors and add cach…
tofikwest Apr 10, 2026
c50e8c0
Merge branch 'main' into tofik/q1-cloud-tests-v2
tofikwest Apr 10, 2026
de30e65
fix(cloud-security): validate poll URLs and fix audit log FK violation
tofikwest Apr 10, 2026
952eef1
Merge branch 'main' into tofik/q1-cloud-tests-v2
tofikwest Apr 10, 2026
85f9fa6
fix(cloud-security): don't re-enable user-disabled services on scan
tofikwest Apr 10, 2026
8077963
fix(cloud-security): fix no-op auto-enable and undefined step in vali…
tofikwest Apr 10, 2026
2edb05e
fix(cloud-security): write scan audit logs for session users
tofikwest Apr 10, 2026
33eab2e
fix(cloud-security): map 'info' severity to 'low' risk in fallback plans
tofikwest Apr 10, 2026
0cdd2f1
fix(cloud-security): tighten Azure provider namespace regex
tofikwest Apr 10, 2026
96a5e0c
fix: restore .superpowers/* gitignore and separate .claude/worktrees
tofikwest Apr 10, 2026
96b20ac
Merge branch 'main' into tofik/q1-cloud-tests-v2
tofikwest Apr 10, 2026
0fdf078
fix: remove root-level AWS SDK pins that break app's s3-request-presi…
tofikwest Apr 10, 2026
75b66e0
fix: align app AWS SDK versions to prevent @smithy/types mismatch
tofikwest Apr 10, 2026
5a122c1
fix(cloud-security): fix IAM baseline service ID mismatch
tofikwest Apr 10, 2026
00e5d2d
Merge branch 'main' into tofik/q1-cloud-tests-v2
tofikwest Apr 10, 2026
4372e40
fix: pin client-s3 and s3-request-presigner to 3.1013.0
tofikwest Apr 10, 2026
cc7f2a2
Merge branch 'main' into tofik/q1-cloud-tests-v2
tofikwest Apr 10, 2026
0def030
fix(cloud-security): clone rollback step params before execution
tofikwest Apr 10, 2026
bde6fcc
fix: workaround S3 presigner type mismatch from duplicate @smithy copies
tofikwest Apr 10, 2026
efdfac4
fix: cast getSignedUrl through unknown to bypass private property check
tofikwest Apr 10, 2026
c2eec97
fix(cloud-security): type batch-fix API response to fix Vercel build
tofikwest Apr 10, 2026
ee9ba3e
fix(cloud-security): add needs_permissions to FindingStatus type
tofikwest Apr 10, 2026
31339c5
fix(cloud-security): add retrying and waiting_for_permissions to Batc…
tofikwest Apr 10, 2026
d9f7577
fix(cloud-security): add all missing fields to BatchRemediationDialog…
tofikwest Apr 10, 2026
f27402d
Merge branch 'main' into tofik/q1-cloud-tests-v2
tofikwest Apr 10, 2026
02efe58
fix(cloud-security): replace undeclared findingsResponse with onComplete
tofikwest Apr 10, 2026
dcce094
fix(cloud-security): fix PROVIDER_FIELDS type for multi-provider support
tofikwest Apr 10, 2026
c3b6828
fix: centralize S3 presigner workaround for all files
tofikwest Apr 10, 2026
827ca8d
fix(cloud-security): use @db/server import in remediate-batch task
tofikwest Apr 10, 2026
aef367b
fix(cloud-security): remove redundant needs_permissions check in retr…
tofikwest Apr 11, 2026
1689394
Merge branch 'main' into tofik/q1-cloud-tests-v2
tofikwest Apr 13, 2026
7920e9b
fix(portal): apply S3 presigner type workaround for portal build
tofikwest Apr 13, 2026
4b001d4
Merge pull request #2493 from trycompai/tofik/q1-cloud-tests-v2
tofikwest Apr 13, 2026
d80fe8d
fix(api): centralize S3 presigner type workaround for API build
tofikwest Apr 13, 2026
8af79ca
fix(api): consolidate duplicate @/app/s3 imports
tofikwest Apr 13, 2026
46ef399
Merge pull request #2520 from trycompai/fix/api-s3-presigner-type-wor…
tofikwest Apr 13, 2026
090eb62
feat(github): add 2FA enforcement check
tofikwest Apr 13, 2026
068e487
fix(api): use relative path for dynamic import of S3 presigner
tofikwest Apr 13, 2026
5efbe13
Merge pull request #2523 from trycompai/fix/api-dynamic-import-s3-pre…
tofikwest Apr 13, 2026
667809d
fix: keep read:org scope, don't escalate to admin:org
tofikwest Apr 13, 2026
124112a
fix(api): add .js extension for NodeNext dynamic s3 import
tofikwest Apr 13, 2026
a6d2625
Merge pull request #2525 from trycompai/fix/api-build-nodenext-s3-import
tofikwest Apr 13, 2026
50bc650
fix(github): handle org-owner 2FA filter failures
tofikwest Apr 13, 2026
901e45d
Merge branch 'main' into feat/github-2fa-check
tofikwest Apr 13, 2026
592cea7
perf(github): avoid full member scan for 2fa summary
tofikwest Apr 13, 2026
558ae63
fix(github): classify 2fa check permission errors precisely
tofikwest Apr 13, 2026
30d09c4
feat(github): include full 2fa username list in summary
tofikwest Apr 13, 2026
a17674c
fix(app): clarify integration setup CTA and guard empty setup forms
tofikwest Apr 13, 2026
78b415c
Merge pull request #2521 from trycompai/feat/github-2fa-check
tofikwest Apr 13, 2026
0b2e26e
fix(app): keep dynamic integrations connectable without setup fields
tofikwest Apr 13, 2026
bde584f
Merge branch 'main' into fix/integrations-setup-cta
tofikwest Apr 13, 2026
885d5d0
Merge pull request #2526 from trycompai/fix/integrations-setup-cta
tofikwest Apr 13, 2026
fceaaa8
feat(documents): add warning alerts in CompanySubmissionWizard for pr…
github-actions[bot] Apr 13, 2026
8785c18
feat(tasks): add approverId to task update in SingleTask component (#…
github-actions[bot] Apr 13, 2026
442cc0b
feat(app): flag old cloud connections for reconnect
tofikwest Apr 13, 2026
fdfc5eb
fix(app): make cloud reconnect cutoff exclusive
tofikwest Apr 13, 2026
a5f59a7
Merge branch 'main' into fix/integrations-setup-cta
tofikwest Apr 13, 2026
27a006f
Merge pull request #2527 from trycompai/fix/integrations-setup-cta
tofikwest Apr 13, 2026
fd9e041
fix(app): unblock GCP reconnect flow from integrations detail
tofikwest Apr 13, 2026
236b357
Merge pull request #2528 from trycompai/fix/gcp-reconnect-flow
tofikwest Apr 13, 2026
79af8b2
fix(frameworks): add PCI DSS Level 1 badge mapping (#2529)
Marfuen Apr 13, 2026
3df961a
Merge branch 'release' into main
Marfuen Apr 13, 2026
4672ea2
fix(cloud): correctly flag legacy connections for reconnect
tofikwest Apr 13, 2026
501b9ad
chore: merge release v3.21.1 back to main [skip ci]
github-actions[bot] Apr 13, 2026
59167fd
fix(app): align reconnect cutoff with rollout timestamp
tofikwest Apr 13, 2026
9756b29
Merge branch 'main' into fix/gcp-reconnect-flow
tofikwest Apr 13, 2026
10b3959
fix(trust): update PCI DSS trust portal badge icon
Marfuen Apr 13, 2026
f3f6263
Merge branch 'main' into fix/gcp-reconnect-flow
tofikwest Apr 13, 2026
68812a2
Merge branch 'release' into main
Marfuen Apr 13, 2026
c1d2b7e
chore: merge release v3.21.2 back to main [skip ci]
github-actions[bot] Apr 13, 2026
de1e1b2
Merge branch 'main' into fix/gcp-reconnect-flow
tofikwest Apr 13, 2026
48c5fe5
Merge pull request #2532 from trycompai/fix/gcp-reconnect-flow
tofikwest Apr 13, 2026
47e78bf
fix(tasks): show integration monitor service names
tofikwest Apr 13, 2026
4faab40
fix(cloud): clear reconnect warning after successful OAuth reconnect
tofikwest Apr 13, 2026
6a0397a
Merge pull request #2537 from trycompai/fix/cloud-reconnect-oauth-clear
tofikwest Apr 13, 2026
c1ec63e
improve(cloud): make gcp setup guidance data-driven and actionable
tofikwest Apr 13, 2026
65f4b87
Merge branch 'main' into feat/github-2fa-check
tofikwest Apr 13, 2026
95abc27
fix(tasks): fallback monitor name in disconnect dialog
tofikwest Apr 13, 2026
a53768a
Merge pull request #2536 from trycompai/feat/github-2fa-check
tofikwest Apr 13, 2026
a48c33a
fix(cloud): classify getIamPolicy permission errors correctly
tofikwest Apr 13, 2026
3a6692e
Merge branch 'main' into fix/cloud-reconnect-oauth-clear
tofikwest Apr 13, 2026
cec9ba6
Merge pull request #2538 from trycompai/fix/cloud-reconnect-oauth-clear
tofikwest Apr 13, 2026
308f2b3
improve(cloud): clarify required vs optional gcp setup steps
tofikwest Apr 13, 2026
b2446d7
feat(cloud): add actionable resolve flow for gcp setup steps
tofikwest Apr 13, 2026
7c25fe2
Merge branch 'main' into fix/cloud-reconnect-oauth-clear
tofikwest Apr 13, 2026
0d40b2e
fix(cloud): harden gcp setup copy + preserve iam etag
tofikwest Apr 13, 2026
ff7b2ec
Merge pull request #2539 from trycompai/fix/cloud-reconnect-oauth-clear
tofikwest Apr 13, 2026
788fdb0
feat: default policy lists to alphabetical order
tofikwest Apr 13, 2026
f93f401
feat: prioritize vendor-listed integrations
tofikwest Apr 13, 2026
249e478
fix: preserve trust favicon branding in token flows
tofikwest Apr 13, 2026
bcb4ef7
feat: show full framework list in finding type dropdown
tofikwest Apr 13, 2026
ced89c2
Merge branch 'main' into sale-34-policy-sort
tofikwest Apr 13, 2026
f21436a
Merge pull request #2541 from trycompai/sale-34-policy-sort
tofikwest Apr 13, 2026
1b5365b
Merge branch 'main' into sale-28-vendor-integrations-priority
tofikwest Apr 13, 2026
4f26e55
Merge pull request #2540 from trycompai/sale-28-vendor-integrations-p…
tofikwest Apr 13, 2026
a4c3200
fix(cloud): avoid false gcp api-enable failures when already enabled
tofikwest Apr 13, 2026
b8d57f8
Merge branch 'main' into cs-222-trust-favicon
tofikwest Apr 13, 2026
2e5cf0f
refactor: reuse trust favicon signing helper
tofikwest Apr 13, 2026
28f5573
Merge pull request #2542 from trycompai/cs-222-trust-favicon
tofikwest Apr 13, 2026
77625ec
fix(cloud): honor gcp service toggles and preserve detection state
tofikwest Apr 14, 2026
c863826
fix(gcp-setup): detect api permission errors from raw response
tofikwest Apr 14, 2026
e7a9cd1
Merge branch 'main' into fix/cloud-reconnect-oauth-clear
tofikwest Apr 14, 2026
2632b5d
fix(cloud-tests): keep gcp setup guide stable during focus revalidation
tofikwest Apr 14, 2026
3d66c52
Merge pull request #2544 from trycompai/fix/cloud-reconnect-oauth-clear
tofikwest Apr 14, 2026
fe27546
fix(gcp): capability-based setup checks and stable provider connectio…
tofikwest Apr 14, 2026
39e38b0
fix(integrations): default gcp services to enabled before detection data
tofikwest Apr 14, 2026
faaf422
Merge branch 'main' into fix/cloud-reconnect-oauth-clear
tofikwest Apr 14, 2026
c84480e
Merge pull request #2545 from trycompai/fix/cloud-reconnect-oauth-clear
tofikwest Apr 14, 2026
e31be9b
Merge branch 'main' into cs-241-framework-dropdown
tofikwest Apr 14, 2026
84ddb7e
Merge pull request #2543 from trycompai/cs-241-framework-dropdown
tofikwest Apr 14, 2026
0e2c1e1
fix(gcp-ux): show detection state and keep setup status accessible
tofikwest Apr 14, 2026
c818277
Merge branch 'main' into fix/cloud-reconnect-oauth-clear
tofikwest Apr 14, 2026
223b83f
fix(types): align connection services api response with SWR contract
tofikwest Apr 14, 2026
8854953
Merge pull request #2546 from trycompai/fix/cloud-reconnect-oauth-clear
tofikwest Apr 14, 2026
e567ba1
feat(gcp): multi-project scoping for scans, services, and remediation
tofikwest Apr 14, 2026
0a7993b
chore: formatting + cursor agent changes from previous sessions
tofikwest Apr 14, 2026
e96e850
fix: address PR review feedback
tofikwest Apr 14, 2026
b61b0db
Merge pull request #2547 from trycompai/feat/gcp-multi-project-scoping
tofikwest Apr 14, 2026
0ad7d7a
feat(integrations): add tooltip to "+N more" badge showing remaining …
tofikwest Apr 14, 2026
f25649d
fix(integrations): hide AI Agent integrations from integrations list
tofikwest Apr 14, 2026
59bf8fa
Merge pull request #2549 from trycompai/fix/integrations-ui-cleanup
tofikwest Apr 14, 2026
1813085
fix: wrap Tooltip in TooltipProvider to prevent runtime error
tofikwest Apr 14, 2026
2a4393b
Merge pull request #2551 from trycompai/fix/tooltip-provider
tofikwest Apr 14, 2026
b63823f
fix(integrations): sort connected integrations to top of the list
tofikwest Apr 14, 2026
4ef60e7
fix(integrations): make tooltip task names clickable links
tofikwest Apr 14, 2026
6e17ff3
fix: treat pending/error/paused connections as established in sort
tofikwest Apr 14, 2026
fe5d024
Merge branch 'main' into fix/tooltip-provider
tofikwest Apr 14, 2026
fec7bbb
Merge pull request #2552 from trycompai/fix/tooltip-provider
tofikwest Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,5 @@ scripts/sync-release-branch.sh

.claude/audit-findings.md

.superpowers/*
.superpowers/*
.claude/worktrees/
51 changes: 49 additions & 2 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,57 @@
"@ai-sdk/groq": "^2.0.32",
"@ai-sdk/openai": "^2.0.65",
"@aws-sdk/client-ec2": "^3.911.0",
"@aws-sdk/client-s3": "^3.859.0",
"@aws-sdk/client-s3": "3.1013.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AWS SDK S3 version pinned without caret causes mismatch

Medium Severity

@aws-sdk/client-s3 and @aws-sdk/s3-request-presigner are pinned to exact 3.1013.0 (no ^) while every other AWS SDK package uses ^3.948.0. AWS SDK v3 packages share internal @smithy/* middleware dependencies. Pinning S3 at a much higher exact version while other clients can float creates a risk of @smithy/* version duplication or incompatibility at runtime, potentially causing serialization or middleware stack errors.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4b001d4. Configure here.

"@aws-sdk/client-acm": "^3.948.0",
"@aws-sdk/client-backup": "^3.948.0",
"@aws-sdk/client-cloudtrail": "^3.948.0",
"@aws-sdk/client-cloudwatch": "^3.948.0",
"@aws-sdk/client-cost-explorer": "^3.948.0",
"@aws-sdk/client-cloudwatch-logs": "^3.948.0",
"@aws-sdk/client-config-service": "^3.948.0",
"@aws-sdk/client-dynamodb": "^3.948.0",
"@aws-sdk/client-ecr": "^3.948.0",
"@aws-sdk/client-ecs": "^3.948.0",
"@aws-sdk/client-efs": "^3.948.0",
"@aws-sdk/client-eks": "^3.948.0",
"@aws-sdk/client-elastic-load-balancing-v2": "^3.948.0",
"@aws-sdk/client-guardduty": "^3.948.0",
"@aws-sdk/client-iam": "^3.948.0",
"@aws-sdk/client-inspector2": "^3.948.0",
"@aws-sdk/client-kms": "^3.948.0",
"@aws-sdk/client-lambda": "^3.948.0",
"@aws-sdk/client-macie2": "^3.948.0",
"@aws-sdk/client-opensearch": "^3.948.0",
"@aws-sdk/client-rds": "^3.948.0",
"@aws-sdk/client-redshift": "^3.948.0",
"@aws-sdk/client-route-53": "^3.948.0",
"@aws-sdk/client-secrets-manager": "^3.948.0",
"@aws-sdk/client-securityhub": "^3.948.0",
"@aws-sdk/client-sns": "^3.948.0",
"@aws-sdk/client-sqs": "^3.948.0",
"@aws-sdk/client-wafv2": "^3.948.0",
"@aws-sdk/client-api-gateway": "^3.948.0",
"@aws-sdk/client-apigatewayv2": "^3.948.0",
"@aws-sdk/client-appflow": "^3.948.0",
"@aws-sdk/client-athena": "^3.948.0",
"@aws-sdk/client-cloudfront": "^3.948.0",
"@aws-sdk/client-codebuild": "^3.948.0",
"@aws-sdk/client-cognito-identity-provider": "^3.948.0",
"@aws-sdk/client-elastic-beanstalk": "^3.948.0",
"@aws-sdk/client-elasticache": "^3.948.0",
"@aws-sdk/client-emr": "^3.948.0",
"@aws-sdk/client-eventbridge": "^3.948.0",
"@aws-sdk/client-glue": "^3.948.0",
"@aws-sdk/client-kafka": "^3.948.0",
"@aws-sdk/client-kinesis": "^3.948.0",
"@aws-sdk/client-network-firewall": "^3.948.0",
"@aws-sdk/client-sagemaker": "^3.948.0",
"@aws-sdk/client-sfn": "^3.948.0",
"@aws-sdk/client-shield": "^3.948.0",
"@aws-sdk/client-ssm": "^3.948.0",
"@aws-sdk/client-sts": "^3.948.0",
"@aws-sdk/s3-request-presigner": "^3.859.0",
"@aws-sdk/client-transfer": "^3.948.0",
"@aws-sdk/s3-request-presigner": "3.1013.0",
"@browserbasehq/sdk": "2.6.0",
"@browserbasehq/stagehand": "^3.0.5",
"@mendable/firecrawl-js": "^4.9.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,36 @@ jest.mock('@db', () => ({
},
Prisma: {},
db: {
auditLog: { get create() { return mockCreate; } },
policy: { get findFirst() { return mockPolicyFind; } },
taskItem: { get findFirst() { return mockTaskFind; } },
vendor: { get findFirst() { return mockVendorFind; } },
finding: { get findFirst() { return mockFindingFind; } },
context: { get findFirst() { return mockContextFind; } },
auditLog: {
get create() {
return mockCreate;
},
},
policy: {
get findFirst() {
return mockPolicyFind;
},
},
taskItem: {
get findFirst() {
return mockTaskFind;
},
},
vendor: {
get findFirst() {
return mockVendorFind;
},
},
finding: {
get findFirst() {
return mockFindingFind;
},
},
context: {
get findFirst() {
return mockContextFind;
},
},
},
}));

Expand Down
8 changes: 6 additions & 2 deletions apps/api/src/admin-organizations/admin-context.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ export class AdminContextController {
}

@Post(':orgId/context')
@ApiOperation({ summary: 'Create a context entry for an organization (admin)' })
@ApiOperation({
summary: 'Create a context entry for an organization (admin)',
})
@UsePipes(
new ValidationPipe({
whitelist: true,
Expand All @@ -59,7 +61,9 @@ export class AdminContextController {
}

@Patch(':orgId/context/:contextId')
@ApiOperation({ summary: 'Update a context entry for an organization (admin)' })
@ApiOperation({
summary: 'Update a context entry for an organization (admin)',
})
@UsePipes(
new ValidationPipe({
whitelist: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ describe('AdminEvidenceController', () => {
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AdminEvidenceController],
providers: [
{ provide: EvidenceFormsService, useValue: mockService },
],
providers: [{ provide: EvidenceFormsService, useValue: mockService }],
}).compile();

controller = module.get<AdminEvidenceController>(AdminEvidenceController);
Expand Down
16 changes: 10 additions & 6 deletions apps/api/src/admin-organizations/admin-evidence.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ import {
@UseInterceptors(AdminAuditLogInterceptor)
@Throttle({ default: { ttl: 60000, limit: 30 } })
export class AdminEvidenceController {
constructor(
private readonly evidenceFormsService: EvidenceFormsService,
) {}
constructor(private readonly evidenceFormsService: EvidenceFormsService) {}

@Get(':orgId/evidence-forms')
@ApiOperation({ summary: 'List evidence form statuses for an organization (admin)' })
@ApiOperation({
summary: 'List evidence form statuses for an organization (admin)',
})
async listFormStatuses(@Param('orgId') orgId: string) {
return this.evidenceFormsService.getFormStatuses(orgId);
}
Expand All @@ -53,8 +53,12 @@ export class AdminEvidenceController {
authContext: buildPlatformAdminAuthContext(req.userId, orgId),
formType,
search,
limit: limit ? String(Math.min(200, Math.max(1, parseInt(limit, 10) || 1))) : undefined,
offset: offset ? String(Math.max(0, parseInt(offset, 10) || 0)) : undefined,
limit: limit
? String(Math.min(200, Math.max(1, parseInt(limit, 10) || 1)))
: undefined,
offset: offset
? String(Math.max(0, parseInt(offset, 10) || 0))
: undefined,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ export class AdminFindingsController {

@Get(':orgId/findings')
@ApiOperation({ summary: 'List all findings for an organization (admin)' })
async list(
@Param('orgId') orgId: string,
@Query('status') status?: string,
) {
async list(@Param('orgId') orgId: string, @Query('status') status?: string) {
let validatedStatus: FindingStatus | undefined;
if (status) {
if (!Object.values(FindingStatus).includes(status as FindingStatus)) {
Expand Down
16 changes: 4 additions & 12 deletions apps/api/src/admin-organizations/admin-guard-integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,7 @@ describe('PlatformAdminGuard — runtime rejection scenarios', () => {
});
const ctx = buildContext({ cookie: 'session=valid' });

await expect(guard.canActivate(ctx)).rejects.toThrow(
ForbiddenException,
);
await expect(guard.canActivate(ctx)).rejects.toThrow(ForbiddenException);
await expect(guard.canActivate(ctx)).rejects.toThrow(
'Access denied: Platform admin privileges required',
);
Expand All @@ -116,9 +114,7 @@ describe('PlatformAdminGuard — runtime rejection scenarios', () => {
});
const ctx = buildContext({ cookie: 'session=valid' });

await expect(guard.canActivate(ctx)).rejects.toThrow(
ForbiddenException,
);
await expect(guard.canActivate(ctx)).rejects.toThrow(ForbiddenException);
});

it('rejects a user with role "owner" (org role, not platform admin)', async () => {
Expand All @@ -130,9 +126,7 @@ describe('PlatformAdminGuard — runtime rejection scenarios', () => {
});
const ctx = buildContext({ cookie: 'session=valid' });

await expect(guard.canActivate(ctx)).rejects.toThrow(
ForbiddenException,
);
await expect(guard.canActivate(ctx)).rejects.toThrow(ForbiddenException);
});

it('rejects when session claims admin but DB says user', async () => {
Expand All @@ -146,9 +140,7 @@ describe('PlatformAdminGuard — runtime rejection scenarios', () => {
});
const ctx = buildContext({ authorization: 'Bearer valid' });

await expect(guard.canActivate(ctx)).rejects.toThrow(
ForbiddenException,
);
await expect(guard.canActivate(ctx)).rejects.toThrow(ForbiddenException);
expect(mockFindUnique).toHaveBeenCalledWith({
where: { id: 'usr_sneaky' },
select: { id: true, email: true, role: true },
Expand Down
52 changes: 42 additions & 10 deletions apps/api/src/admin-organizations/admin-organizations.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,25 @@ export class AdminOrganizationsController {
}

@Get('activity')
@ApiOperation({ summary: 'Organization activity report - shows last session per org (platform admin)' })
@ApiQuery({ name: 'inactiveDays', required: false, description: 'Filter orgs with no session in N days (default: 90)' })
@ApiQuery({ name: 'hasAccess', required: false, description: 'Filter by hasAccess (true/false)' })
@ApiQuery({ name: 'onboarded', required: false, description: 'Filter by onboardingCompleted (true/false)' })
@ApiOperation({
summary:
'Organization activity report - shows last session per org (platform admin)',
})
@ApiQuery({
name: 'inactiveDays',
required: false,
description: 'Filter orgs with no session in N days (default: 90)',
})
@ApiQuery({
name: 'hasAccess',
required: false,
description: 'Filter by hasAccess (true/false)',
})
@ApiQuery({
name: 'onboarded',
required: false,
description: 'Filter by onboardingCompleted (true/false)',
})
@ApiQuery({ name: 'page', required: false })
@ApiQuery({ name: 'limit', required: false })
async activity(
Expand All @@ -57,9 +72,16 @@ export class AdminOrganizationsController {
@Query('limit') limit?: string,
) {
return this.service.getOrgActivity({
inactiveDays: Math.max(0, Number.isFinite(parseInt(inactiveDays ?? '90', 10)) ? parseInt(inactiveDays ?? '90', 10) : 90),
hasAccess: hasAccess === 'true' ? true : hasAccess === 'false' ? false : undefined,
onboarded: onboarded === 'true' ? true : onboarded === 'false' ? false : undefined,
inactiveDays: Math.max(
0,
Number.isFinite(parseInt(inactiveDays ?? '90', 10))
? parseInt(inactiveDays ?? '90', 10)
: 90,
),
hasAccess:
hasAccess === 'true' ? true : hasAccess === 'false' ? false : undefined,
onboarded:
onboarded === 'true' ? true : onboarded === 'false' ? false : undefined,
page: Math.max(1, parseInt(page || '1', 10) || 1),
limit: Math.min(100, Math.max(1, parseInt(limit || '50', 10) || 50)),
});
Expand Down Expand Up @@ -109,9 +131,19 @@ export class AdminOrganizationsController {
}

@Get(':id/audit-logs')
@ApiOperation({ summary: 'Get audit logs for an organization (platform admin)' })
@ApiQuery({ name: 'entityType', required: false, description: 'Filter by entity type (e.g. policy, task)' })
@ApiQuery({ name: 'take', required: false, description: 'Number of logs to return (max 100, default 100)' })
@ApiOperation({
summary: 'Get audit logs for an organization (platform admin)',
})
@ApiQuery({
name: 'entityType',
required: false,
description: 'Filter by entity type (e.g. policy, task)',
})
@ApiQuery({
name: 'take',
required: false,
description: 'Number of logs to return (max 100, default 100)',
})
async getAuditLogs(
@Param('id') id: string,
@Query('entityType') entityType?: string,
Expand Down
21 changes: 17 additions & 4 deletions apps/api/src/admin-organizations/admin-organizations.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,14 @@ export class AdminOrganizationsService {
createdAt: true,
hasAccess: true,
onboardingCompleted: true,
_count: { select: { members: true, tasks: true, policy: true, auditLog: true } },
_count: {
select: {
members: true,
tasks: true,
policy: true,
auditLog: true,
},
},
members: {
where: { deactivated: false },
select: {
Expand Down Expand Up @@ -168,14 +175,20 @@ export class AdminOrganizationsService {
lastSession = sess;
}
if (member.role?.includes('owner') && !owner) {
owner = { id: member.user.id, name: member.user.name, email: member.user.email };
owner = {
id: member.user.id,
name: member.user.name,
email: member.user.email,
};
}
}

const lastAuditLog = org.auditLog?.[0]?.timestamp ?? null;
const lastActivity = [lastSession, lastAuditLog]
.filter(Boolean)
.sort((a, b) => (b as Date).getTime() - (a as Date).getTime())[0] as Date | undefined;
.sort((a, b) => (b as Date).getTime() - (a as Date).getTime())[0] as
| Date
| undefined;

const isActive = lastActivity ? lastActivity >= cutoff : false;

Expand All @@ -191,7 +204,7 @@ export class AdminOrganizationsService {
auditLogCount: org._count.auditLog,
owner,
lastSession: lastSession?.toISOString() ?? null,
lastAuditLog: lastAuditLog ? (lastAuditLog as Date).toISOString() : null,
lastAuditLog: lastAuditLog ? lastAuditLog.toISOString() : null,
lastActivity: lastActivity?.toISOString() ?? null,
isActive,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ jest.mock('@db', () => ({

jest.mock('@trigger.dev/sdk', () => ({
auth: {
createPublicToken: jest
.fn()
.mockResolvedValue('mock-public-access-token'),
createPublicToken: jest.fn().mockResolvedValue('mock-public-access-token'),
},
tasks: {
trigger: jest.fn().mockResolvedValue({ id: 'run_123' }),
Expand Down Expand Up @@ -85,9 +83,9 @@ describe('AdminPoliciesController', () => {
});

it('should reject missing status', async () => {
await expect(
controller.update('org_1', 'pol_1', {}),
).rejects.toThrow(BadRequestException);
await expect(controller.update('org_1', 'pol_1', {})).rejects.toThrow(
BadRequestException,
);
});

it('should reject invalid status', async () => {
Expand Down
8 changes: 2 additions & 6 deletions apps/api/src/admin-organizations/admin-policies.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ export class AdminPoliciesController {
const updateData: Record<string, unknown> = {};

if (body.status !== undefined) {
if (
!Object.values(PolicyStatus).includes(body.status as PolicyStatus)
) {
if (!Object.values(PolicyStatus).includes(body.status as PolicyStatus)) {
throw new BadRequestException(
`Invalid status. Must be one of: ${Object.values(PolicyStatus).join(', ')}`,
);
Expand Down Expand Up @@ -135,9 +133,7 @@ export class AdminPoliciesController {
});

const uniqueFrameworks = Array.from(
new Map(
instances.map((fi) => [fi.framework.id, fi.framework]),
).values(),
new Map(instances.map((fi) => [fi.framework.id, fi.framework])).values(),
).map((f) => ({
id: f.id,
name: f.name,
Expand Down
Loading
Loading