Conversation
… events (#3508) * fix(audit): stop logging read endpoints as mutations + name task-item events Two audit-trail hygiene fixes: - The global AuditLogInterceptor derived the verb purely from the HTTP method, so read endpoints that use POST to carry a body (e.g. `POST .../list`, `POST .../status`) were logged as "Created X" on every page load — spamming the trail with false "Created trust"/"Created integration" entries. Skip logging when the endpoint's declared permission is read-only (`['read']`); `@AuditRead` still opts a read endpoint back in. - Task-item audit descriptions said "created this task" / "deleted this task" — wording written for a single task's own timeline that reads ambiguously in the org-wide feed. Name the task instead: `Created task "<title>"` / `Deleted task "<title>"`, across the audit service and the vendor risk-assessment flow that emits the same events. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(audit): only skip audit when ALL declared permissions are read-only Addresses cubic review: the read-only skip checked only the first @RequirePermission, so a POST declaring multiple requirements (e.g. [read, create]) would drop its mutation audit entry. Skip only when every declared permission is exclusively `read`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
…stay visible (#3510) Co-authored-by: chasprowebdev <chasgarciaprowebdev@gmail.com> Co-authored-by: chasprowebdev <70908289+chasprowebdev@users.noreply.github.com>
…bmitted (CS-522) (#3494) * feat(api): email org owners/admins when a portal access request is submitted * fix(api): fix stale isActive filter assertion in notifier spec --------- Co-authored-by: chasprowebdev <chasgarciaprowebdev@gmail.com> Co-authored-by: chasprowebdev <70908289+chasprowebdev@users.noreply.github.com> Co-authored-by: Tofik Hasanov <72318342+tofikwest@users.noreply.github.com>
…Tiptap JSON (#3409) * fix(api): validate comment length against visible text, not raw Tiptap JSON * fix(api): only treat content as Tiptap JSON when it has a doc shape * fix(api): reject empty Tiptap documents in comment content validation * fix(api): stop double-counting line breaks inside blockquotes * fix(api): document raw payload maxLength on comment content in OpenAPI schema * fix(api): count Unicode code points to keep the limit aligned with visible text --------- Co-authored-by: chasprowebdev <chasgarciaprowebdev@gmail.com> Co-authored-by: chasprowebdev <70908289+chasprowebdev@users.noreply.github.com> Co-authored-by: Tofik Hasanov <72318342+tofikwest@users.noreply.github.com>
There was a problem hiding this comment.
1 issue found across 12 files (changes from recent commits).
Confidence score: 2/5
- In
apps/api/src/evidence-forms/evidence-forms-notifier.service.ts, recipient selection based on role names containingadmin/ownercan send access-request details to roles without review permission (e.g.,admin-assistant), creating a concrete unauthorized disclosure risk — resolve recipients via RBAC permission checks (for example throughRolesService) rather than name matching.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/api/src/evidence-forms/evidence-forms-notifier.service.ts">
<violation number="1" location="apps/api/src/evidence-forms/evidence-forms-notifier.service.ts:137">
P1: Access-request details are emailed to any custom role whose name contains `admin` or `owner`, even if it has no review permission (for example, `admin-assistant`). Resolve recipients through RBAC (such as `RolesService.filterMembersWithPermission` for the submission-review permission) rather than matching role-name substrings.
(Based on your team's feedback about RBAC permission checks.) [FEEDBACK_USED]</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
| const seen = new Set<string>(); | ||
| const recipients: Recipient[] = []; | ||
| for (const member of members) { | ||
| if (!member.role.includes('admin') && !member.role.includes('owner')) { |
There was a problem hiding this comment.
P1: Access-request details are emailed to any custom role whose name contains admin or owner, even if it has no review permission (for example, admin-assistant). Resolve recipients through RBAC (such as RolesService.filterMembersWithPermission for the submission-review permission) rather than matching role-name substrings.
(Based on your team's feedback about RBAC permission checks.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/evidence-forms/evidence-forms-notifier.service.ts, line 137:
<comment>Access-request details are emailed to any custom role whose name contains `admin` or `owner`, even if it has no review permission (for example, `admin-assistant`). Resolve recipients through RBAC (such as `RolesService.filterMembersWithPermission` for the submission-review permission) rather than matching role-name substrings.
(Based on your team's feedback about RBAC permission checks.) </comment>
<file context>
@@ -0,0 +1,157 @@
+ const seen = new Set<string>();
+ const recipients: Recipient[] = [];
+ for (const member of members) {
+ if (!member.role.includes('admin') && !member.role.includes('owner')) {
+ continue;
+ }
</file context>
…fixed on the Trust Portal settings (#3247) * fix(api): add daily domain health check scheduled task * fix(api): correct maxDuration and continue batch on per-domain vercel api failure instead of aborting in domain check schedule * fix(api): route domain misconfigured email through trust portal channel * fix(api): remove unused TRUST_PORTAL_PROJECT_ID requirement from vercel config check * fix(api): parallelize domain health checks and email sends * fix(api): isolate per-trust domain health check failures with Promise.allSettled * fix(api): use exact role matching for domain health notifications --------- Co-authored-by: chasprowebdev <chasgarciaprowebdev@gmail.com> Co-authored-by: chasprowebdev <70908289+chasprowebdev@users.noreply.github.com> Co-authored-by: Tofik Hasanov <72318342+tofikwest@users.noreply.github.com> Co-authored-by: Mariano Fuentes <marfuen98@gmail.com>
There was a problem hiding this comment.
2 issues found across 3 files (changes from recent commits).
Confidence score: 3/5
- In
apps/api/src/trigger/trust-portal/check-domain-health-schedule.ts, clearing trust state before confirming email delivery means a transient send failure can permanently suppress future domain-fix notifications, so users may never be alerted to remediation steps — persist notification status with retries (or only clear trust after a confirmed send). - In
apps/api/src/trigger/trust-portal/check-domain-health-schedule.ts, recipient selection is based on role name instead of effectivetrust:updatepermission, so eligible custom-role users can be skipped and miss critical remediation emails — resolve recipients from computed permissions rather than role-name matching.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/api/src/trigger/trust-portal/check-domain-health-schedule.ts">
<violation number="1" location="apps/api/src/trigger/trust-portal/check-domain-health-schedule.ts:124">
P2: A transient email failure permanently loses the domain-fix notification: this update removes the trust from all later checks before failed sends can be retried. Persist notification delivery/retry state or defer clearing `domainVerified` until notification delivery is recoverable.</violation>
<violation number="2" location="apps/api/src/trigger/trust-portal/check-domain-health-schedule.ts:129">
P2: Custom roles granted `trust:update` never receive this remediation email, while a role-name match determines delivery instead of the Trust Portal permission. Resolve each member's effective permissions and select recipients with `trust:update`.
(Based on your team's feedback about RBAC permission checks.) [FEEDBACK_USED]</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
|
|
||
| await db.trust.update({ | ||
| where: { organizationId: trust.organizationId }, | ||
| data: { domainVerified: false }, |
There was a problem hiding this comment.
P2: A transient email failure permanently loses the domain-fix notification: this update removes the trust from all later checks before failed sends can be retried. Persist notification delivery/retry state or defer clearing domainVerified until notification delivery is recoverable.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/trigger/trust-portal/check-domain-health-schedule.ts, line 124:
<comment>A transient email failure permanently loses the domain-fix notification: this update removes the trust from all later checks before failed sends can be retried. Persist notification delivery/retry state or defer clearing `domainVerified` until notification delivery is recoverable.</comment>
<file context>
@@ -0,0 +1,196 @@
+
+ await db.trust.update({
+ where: { organizationId: trust.organizationId },
+ data: { domainVerified: false },
+ });
+
</file context>
|
|
||
| const adminOrOwnerMembers = trust.organization.members.filter( | ||
| (m) => | ||
| parseRoles(m.role).some((role) => NOTIFIABLE_ROLES.includes(role)) && |
There was a problem hiding this comment.
P2: Custom roles granted trust:update never receive this remediation email, while a role-name match determines delivery instead of the Trust Portal permission. Resolve each member's effective permissions and select recipients with trust:update.
(Based on your team's feedback about RBAC permission checks.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/trigger/trust-portal/check-domain-health-schedule.ts, line 129:
<comment>Custom roles granted `trust:update` never receive this remediation email, while a role-name match determines delivery instead of the Trust Portal permission. Resolve each member's effective permissions and select recipients with `trust:update`.
(Based on your team's feedback about RBAC permission checks.) </comment>
<file context>
@@ -0,0 +1,196 @@
+
+ const adminOrOwnerMembers = trust.organization.members.filter(
+ (m) =>
+ parseRoles(m.role).some((role) => NOTIFIABLE_ROLES.includes(role)) &&
+ m.user?.email,
+ );
</file context>
* fix(audit): stop plaintext secrets/credentials leaking into the audit log
Audit rows are readable with app:read (e.g. the auditor role), but the
interceptor diffed request bodies into AuditLog.data with only exact-match,
top-level key redaction — so credentials landed in cleartext where roles denied
the resource's own read permission could see them.
Confirmed leak: POST/PUT /v1/secrets stored the plaintext `value` (key not in
the denylist), readable by an auditor who is intentionally denied secret:read.
Defense in depth, all layers:
- Per-resource body skip (REDACT_BODY_RESOURCES = {secret}): log the action, not
the payload, for credential resources whose secret rides in a generic field.
- Regex key matching: redact clientSecret, secretAccessKey, accessKeyId, etc.
that the exact-match SENSITIVE_KEYS set missed.
- Summarize object elements inside arrays as [Object] (as nested objects already
are), so secrets in generic array fields (browserbase extraFields[].value,
email attachments[].content) can't be logged verbatim.
- @SkipAuditLog the internal mailer (html/attachments carry OTP/magic-links).
Tests: new audit-log.utils.spec (regex, arrays, nested); interceptor asserts a
secret mutation logs the action but never the plaintext value.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(audit): apply credential regex in the admin audit interceptor too
Addresses cubic review: AdminAuditLogInterceptor.sanitizeBody only skipped
exact-match SENSITIVE_KEYS, so credential-shaped fields (clientSecret,
secretAccessKey, …) could still leak through the platform-admin audit path.
Reuse the shared SENSITIVE_KEY_PATTERN so both interceptors redact consistently.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
🎉 This PR is included in version 3.110.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This is an automated pull request to release the candidate branch into production, which will trigger a deployment.
It was created by the [Production PR] action.
Summary by cubic
Stops audit spam from read‑only POST endpoints, blocks plaintext secrets from audit logs, and validates comment length against visible text. Adds a daily Trust Portal domain health check that auto-unverifies misconfigured custom domains and notifies owners/admins; also names tasks in audit entries, emails owners/admins on access‑request submissions, and keeps invite‑modal actions visible.
New Features
Bug Fixes
secretbodies, and skipping internal mailer endpoints. Applied across org and admin audit paths.Written for commit 55a48fb. Summary will update on new commits.