Commit Graph

2727 Commits

Author SHA1 Message Date
Cursor Agent
b1e6949fe4 Escape CSV cells that start with tab or carriage return
Trim-based formula detection skipped the old rule for
leading control characters; check those bytes before trim.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:13:17 +00:00
Cursor Agent
90b3258f32 Harden safecsv against whitespace formula tricks
Ignore leading Unicode space when detecting formula cells,
extend starter runes, and document export usage in package
doc.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:13:17 +00:00
Cursor Agent
fc54faa263 Extract safecsv writer for spreadsheet-safe exports
Wrap encoding/csv with formula-safe cell sanitization and
use it from log export streaming.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
c5b1f210d5 Harden log export CSV and fix profile loader boundary
Load SCIM profile emails via Identities in the export
layer, drop the cross-table join, sanitize formula-leading
cells, and fall back to validated userName when profile email
is missing. Drop redundant gid array casts in LoadByIDs.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
45a7a428bf Fix wsl lint in SCIM export string dedupe
Add required blank line before append in
uniqueNonEmptyStrings.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
6906e0ff5b Separate SCIM export email from event user name
Load identity email for matched profiles and drop parsing
userName as email; user_name column stays the event value.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
6cbf16814c Use coredata models in log export batch loaders
Drop export-only row types; load Identity, PersonalAPIKey,
and MembershipProfile through the usual slice LoadByIDs helpers.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
e4d86f0f9e Add narrow audit log actor loaders for CSV export
Replace full Identity and PersonalAPIKey batch loads with
rows that select only id, email, name fields used in export.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
7bba43b902 Select only SCIM export profile columns by user name
The batch loader no longer scans into MembershipProfile with
placeholder email and organization name columns from dropped
identity and organization joins.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
ced25bad32 Load SCIM export profiles without identity join
Membership profile rows already carry SCIM userName and
name fields; export email comes from userName, not login
identity email.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
b715107e6c Drop empty-input guards from batch loaders
ANY(@ids) with an empty array is valid in Postgres and
CollectRows already yields an empty slice; the early nil
assignments added noise without changing callers.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
0e05fca7fc Hoist log export WalkAll order to package vars
Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
018b0d5633 Fold WalkLoader into Loader with slice constraint
Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
0132bc78bd Let WalkAll use coredata collection slice types
WalkAll now accepts loaders and callbacks typed as S ~[]T so
exports can use AuditLogEntries and SCIMEvents directly. Pass the
trimmed page slice to walk, not the over-fetched loader result.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
bff06a3c6d Pass export filters into CSV stream helpers
Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
2026-07-30 10:12:47 +00:00
Cursor Agent
e9567c8e65 Wrap audit log export load errors and rename batch
Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
1bdbe2c1a8 Rename SCIM export page variable to events
Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
a3fb9d3dca Drop JSON blobs from log export CSV rows
Audit exports no longer emit a metadata JSON column. SCIM full
names come only from membership profiles, not request bodies.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Cursor Agent
5cd407bd86 Emit audit and SCIM log exports as CSV
JSONL was awkward in spreadsheets and SIEM imports. Write
tab-separated-friendly CSV with organization name on every row,
resolve audit actors to email or API key name, and enrich SCIM rows
with profile email and display name when available.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 10:12:47 +00:00
Ludovic Vielle
5e63e193cd Stop macOS agent updates from spawning BTM entries
Unsigned darwin release tarballs replaced the Developer ID
binary on each auto-update, so Background Task Management
treated every release as a new identity and showed the generic
executable icon. Sign those archives with a stable identifier,
refuse signature downgrades, and attribute daemon/tray jobs to
Probo Agent.app via AssociatedBundleIdentifiers.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-07-30 11:43:02 +02:00
Cursor Agent
0a61bea40c Fix deactivateUser title after main rebase
Main renamed archiveUser to deactivateUser; keep the new name and
set its title and full annotation hints.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 09:16:06 +00:00
Cursor Agent
7a83f2bc65 Set destructive false on read-only MCP tools
Omiting destructiveHint defaults to true in the MCP spec, so reads
must set it explicitly even though the field is only meaningful when
not read-only.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 09:15:07 +00:00
Cursor Agent
a13dbe1e78 Set full MCP tool annotation hints in schema
Fill in readonly, destructive, idempotent, and openWorld on every
tool so clients can rely on explicit values instead of MCP defaults
(destructive and openWorld both default to true when omitted).

Drop the temporary mcpgen vendor from this change; that lands in a
separate pull request.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-30 09:15:07 +00:00
Cursor Agent
5b47a83160 Annotate MCP tools with titles and hints
Claude and other MCP clients use title, readOnlyHint, and
destructiveHint to present reads, writes, and deletes accurately.
Add a title to every tool, mark missing delete/unlink/cancel/void
tools as destructive, and teach mcpgen to emit those annotations
(including destructiveHint: false for non-destructive writes).

Temporary third_party/mcpgen fork until title support lands
upstream.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
2026-07-30 09:15:07 +00:00
Émile Ré
eecf8a1d97 Make profile state filters multi-value
Address PR review feedback on the profile-state split: SAML sign-in now
activates a pending profile, deactivation counts owners against the
profile's own organization to close a last-owner bypass, the migration
leaves historical activated_at/deactivated_at NULL rather than
fabricating timestamps, and pending members are no longer rendered with
the deactivated (faded) styling.

Drop the single-value state filter in favor of the multi-value states
across the profile and signatory surfaces. Remove ProfileFilter.state
(only states[] remains) and convert the signatures profileState filter
to profileStates. Turn the console people filter, the CLI
"user list --state" flag, and the n8n listUsers and getAllSignatures
state inputs into multi-select controls, where an empty selection means
all states.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-30 09:19:42 +02:00
Émile Ré
b10fc55b7f Rename user archive action to deactivate
"Archive" was misleading for users: the action sets a profile to
DEACTIVATED while keeping the person in the organization. Rename it to
"deactivate" across the API, CLI, MCP, n8n, and console UI.

Consolidate the two overlapping operations into a single deactivateUser
backed by the fuller, guarded logic (SCIM guard, last-active-owner
guard, invitation expiry, signature cancellation, membership update,
webhook) and authorized via iam:membership-profile:deactivate. Remove
the archiveUser surface and the thin state-only deactivate path.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-30 09:19:42 +02:00
Émile Ré
4a276e3ef7 Split inactive profile state
Replace the binary profile ACTIVE/INACTIVE model with PENDING, ACTIVE,
and DEACTIVATED so invited-but-not-yet-activated members remain
assignable to assets, data, and risks instead of being treated like
deactivated users.

Add activated_at/deactivated_at timestamps and Mark* lifecycle helpers,
and update every transition (create, invite/re-invite, activation,
archive, SCIM, SAML, sessions, compliance-portal grant) to the new
states. Expose a multi-state states[] filter across coredata, GraphQL,
MCP, and the console owner pickers, which now request ACTIVE and
PENDING members.

A migration renames the membership_state enum, classifies existing
inactive profiles as PENDING from recent invitation activity, and
backfills the new timestamp columns.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-30 09:19:42 +02:00
Cursor Agent
555b121108 Fix wsl lint in probod cookie wiring
Add a blank line before authSecureCookieConfig so
golangci wsl_v5 passes in lint-go.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 19:09:22 +02:00
Cursor Agent
62d0ab68c4 Make auth cookie SameSite configurable
Add same-site to auth cookie config with lax as the default,
PROBOD_AUTH_COOKIE_SAMESITE bootstrap mapping, and validation
that rejects none unless Secure is enabled.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 19:09:21 +02:00
Sacha Al Himdani
cd6c46212a Add log export for audit logs and SCIM events
Route audit-log and SCIM-event exports through export_jobs with typed
arguments, an iam BuildAndUploadExport/SendExportEmail implementation,
and a concurrent export-job worker with stale recovery. Stream JSONL via
page.WalkAll into S3, and expose the request flow on console, connect,
MCP, and CLI.

Co-authored-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
2026-07-29 18:57:31 +02:00
Émile Ré
ed297ccc9e Allow CORS origins in safeRedirect hosts
Local Vite continue URLs use absolute localhost origins that never
pass verified custom-domain checks. Reuse AllowedOrigins so post-auth
redirects work in dev without disabling Validate.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-29 18:53:54 +02:00
Cursor Agent
4960abab9e Enable tagged PDF export for html2pdf renders
Chrome PrintToPDF now requests a structure tree and document
outline by default, with optional RenderConfig overrides. Add
lang="en" on the e-signature certificate template so /Lang is
emitted.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 18:37:06 +02:00
Cursor Agent
b229da0115 Block delete for in-progress access review campaigns
Reject deletion while a campaign is fetching sources so workers are
not racing a removed record. All other statuses remain deletable.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 16:02:26 +00:00
Cursor Agent
b156b0333a Allow deleting access review campaigns in any status
Drop the backend status gate on campaign delete and show delete in
the console whenever the user has delete permission, regardless of
whether the campaign is draft, in progress, or completed.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 16:02:26 +00:00
Cursor Agent
9c1cecfc1e Allow deleting completed access review campaigns
Completed campaigns were immutable because delete only accepted draft
or cancelled status. Extend deletion to completed records and surface
delete in the console for that status.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 16:02:26 +00:00
Cursor Agent
255bea4738 Make risk delete prove document mapping cleanup
Removing junction rows before deleteRisk hid missing soft-delete
cleanup in e2e. Assert deleteRisk succeeds only after deleteDocument
clears mappings, and document links must be removed first.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 18:00:34 +02:00
Cursor Agent
f451e94c0b Clear document mappings on soft delete
Document soft delete cleared generated-document references but left
control, risk, and measure junction rows pointing at deleted documents.
That blocked risk deletion and made unlinkRisk fail when the document
was already gone.

Remove entity mappings in SoftDelete and BulkSoftDelete, drop mappings
before deleting a risk, tolerate missing documents when unlinking, and
backfill orphaned junction rows for soft-deleted documents.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 18:00:32 +02:00
Ludovic Vielle
ad615a47a0 Show posture values and report history
Pass/fail was the main device UI signal, but operators need
the agent's observed value. Expose a formatted value per
check, show current postures on the device page, and replace
the Postures tab with paginated report history grouped by
agent push time. Status stays in the model for later rulesets.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-07-29 16:27:23 +02:00
Cursor Agent
9e9272b4ac Add sort fields for audit engagement dates
Expose AUDIT_START_DATE and AUDIT_END_DATE on AuditOrderField
so list queries can paginate like valid_from and valid_until.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-28 17:27:34 +02:00
Cursor Agent
c7b1562d26 Store audit engagement dates as DATE columns
Match valid_from and valid_until on audits and the console
date-only inputs for audit start and end.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-28 17:27:34 +02:00
Cursor Agent
f8f5152566 Use timestamptz for audit engagement dates
Store audit_start_date and audit_end_date as TIMESTAMP WITH
TIME ZONE. Alphabetize n8n audit field collections to satisfy
n8n-node lint.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-28 17:27:33 +02:00
Cursor Agent
fe215efbd1 Add audit start and end dates to audits
ISO audits often span a window distinct from certificate validity.
Store optional audit_start_date and audit_end_date on the audit
record and expose them through GraphQL, MCP, CLI, n8n, and console.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-28 17:27:33 +02:00
Cursor Agent
dcb7ffa945 Suppress Mermaid render error artifacts
Configure Mermaid to suppress its built-in error renderer in both
interactive UI previews and generated document HTML. Render failures now
surface through the product toast flow with a safe inline fallback instead
of leaving raw Mermaid diagnostics in the document body.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Émile Ré <nemile.re@gmail.com>
2026-07-28 16:04:07 +02:00
Émile Ré
6e11886365 Extract magic link handler and verify email
Move MagicLinkHandler out of oidc_handler.go. Completing a
magic link for an existing identity now marks the address
verified, matching OIDC behavior.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 15:11:06 +02:00
Émile Ré
b64f101ab9 Drop resend verification email cooldown
Resend only runs after an explicit form submit, so a per-address
cooldown is unnecessary overhead compared with forgot-password.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 15:11:06 +02:00
Émile Ré
82a62005f8 Harden email verification resend against abuse
Add a per-address confirmation-email cooldown and disable the
resend/forgot-password submit buttons while the mutation is in
flight so callers cannot flood the mail queue or double-submit.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 15:11:06 +02:00
Émile Ré
5d0882778f Gate password sign-in on email verification
Unverified password identities were able to open sessions after
signing out. Reject sign-in with EMAIL_NOT_VERIFIED and add a
resend-confirmation flow so users can complete verification.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 15:11:05 +02:00
Émile Ré
319b3ec7fb Add missing generated comments and llm records
Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 10:31:49 +02:00
Bryan Frimin
636dd172e8 Fix wsl_v5 spacing in MergeByCampaignID
Add blank lines around the dedup-and-connector-ID block to satisfy
the wsl_v5 linter (blank line after an if-block, above an assignment
following a block, and above an if with no shared variables in the
preceding line).

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-27 18:56:25 +02:00
Bryan Frimin
b43e66ffa4 Remove restated doc comment on MergeByCampaignID
The comment repeated what the function signature and body already
say plainly.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-27 18:56:25 +02:00