- Fix import order: react-relay before react-router
- Add parentheses around arrow function parameter
- Sync category filter with URL param changes, not just on mount
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
When clicking a category name in the measure detail breadcrumb, users should be taken back to the measures list with that category filtered. Previously, the breadcrumb linked to a route that wasn't read by the measures page.
Changed the breadcrumb to use a ?category search param instead of a route segment, and updated the measures page to initialize and sync its category filter from the URL. Removed the now-unused category/:categoryId route.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Use baseurl.Parse to construct the HTTPS redirect URL in the
trust center HTTP handler, breaking the taint chain from raw
request headers. Apply path.Clean to the slug-based redirect
in stripTrustPrefix to normalize path traversal sequences.
Addresses CodeQL go/unvalidated-url-redirection (CWE-601).
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Adds go-fmt and go-fix Makefile targets that fail when gofmt or go fix
suggest changes. Both are wired into the lint target and used in CI.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
The SAML Assertion Consumer Service endpoint receives cross-origin POSTs from external identity providers by design. Bypass CSRF protection for this specific endpoint since the endpoint validates SAML response signatures itself.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Documents can be archived and unarchived. Archived documents are
read-only, excluded from the trust center, and moved to a dedicated
Archived tab in the document list.
- Add archived_at timestamp and status (ACTIVE/ARCHIVED) PG enum column
- Rename DocumentStatus → DocumentVersionStatus, introduce DocumentStatus
- Archive/unarchive mutations in GraphQL, MCP, and CLI
- Bulk archive/unarchive mutations with Active/Archived tabs in the list
- ABAC policies: write actions denied on archived docs, unarchive denied
on active docs
- Remove control/risk mappings and reset trust center visibility on archive
- Exclude archived documents from mapping dialogs and trust center tab
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
The pg_advisory_xact_lock inside the WITH clause caused race conditions
in READ COMMITTED mode. When a transaction blocked on the lock and resumed
after acquiring it, it used a stale snapshot and computed the same reference
ID as the previous transaction, violating the unique constraint. Moving the
lock to a separate statement before the INSERT ensures the snapshot includes
all previously committed data.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Addresses PR review comments: clears selected document IDs when changing
the type filter to prevent bulk actions on hidden rows, updates the Relay
connection ID when the filter changes so mutations target the correct
connection, and fixes eslint indentation violations.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Adds 5 new document types (PLAN, REGISTER, RECORD, REPORT, TEMPLATE), renames ISMS to GOVERNANCE, and implements type-based filtering across GraphQL, MCP, and frontend. Includes migration, enum updates, filter implementation with SQL array support, and frontend dropdown UI with Relay refetch pattern.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
CountByRiskID and CountByControlID CTEs did not include m.category in their SELECT
lists, causing "column category does not exist" errors when MeasureFilter applied
category constraints. Added m.category to both CTEs.
Also add comprehensive e2e tests for measure filtering by category at organization,
risk, and control levels.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The CountActiveOwnerByOrganizationID query joins iam_membership_profiles with
iam_memberships, but used an unqualified tenant_id = @tenant_id in the WHERE
clause. Since both tables have a tenant_id column, PostgreSQL raised an
"ambiguous column" error when deleting an owner. Fixed by prefixing with the
table alias (p.tenant_id) to match the pattern used in CountByOrganizationID.
Added TestUser_RemoveOwner e2e test to verify one owner can remove another.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Increase sleep time from 10ms to 1100ms to ensure timestamp precision works
consistently across databases with second-level granularity. Strengthen
AssertTimestampsOnUpdate to require strictly increasing timestamps instead of
allowing equal values.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
When creating an organization, the owner's membership profile was being set with the organization name as the full name instead of the owner's actual full name. Fetch the identity's full name and use it for the profile.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Enforce minimum (25) and maximum (100) bounds on cursor size to prevent negative values or excessively large page sizes. All three API surfaces (GraphQL, MCP, CLI) funnel through NewCursor, so the fix belongs here.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Support distinguishing between minor and major non-conformities in findings. Rename the existing NONCONFORMITY enum value to MINOR_NONCONFORMITY and add a new MAJOR_NONCONFORMITY value across all API layers (GraphQL, MCP, CLI) and the database.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Replace the client-side grouped-by-category view (fetching 500 items)
with a flat table using server-side filtering and cursor-based
pagination. Colocate GraphQL queries, fragments, and mutations in the
component file per console CLAUDE.md conventions.
Backend changes add a category filter to the measure list endpoints
(GraphQL, MCP) and a new measureCategories field on Organization.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Remove createPortal and main element ref/state in favor of a simple
fixed overlay with top-12 offset. This avoids React 19 strict mode
violations around ref access during render and setState in effects.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Replace mainRef with useState to avoid accessing ref.current during
render, which is forbidden by React 19's strict ref rules.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Add cleanup function to the useEffect that sets position: relative on
the main element, so the style is restored when the component unmounts.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Clamp the counter with Math.max(0, ...) and use <= 0 check to prevent
the drag overlay from getting stuck when dragLeave fires more than
dragEnter.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Offset the fixed overlay with top-12 so the navigation bar remains
visible and unobstructed during file drag.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The previous approach attached drag handlers to a content div that
didn't fill the viewport, making the dropzone hard to target. Now
drag detection uses window-level events with a counter for correct
nested element handling, and the full-screen overlay itself becomes
the react-dropzone drop target.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
- Move DialogFooter before type DialogRef to satisfy import-x/order
- Check errors argument in onCompleted callback to avoid treating
GraphQL errors as successful uploads
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
- Fix import order for DialogRef type
- Add explicit type parameter to useMutation
- Remove unnecessary parens around single arrow function arg
- Fix jsx-one-expression-per-line for MB text
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Move uploadAuditReportMutation into CreateAuditDialog and replace
promisifyMutation with a Promise wrapper using native Relay callbacks.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Enable users to drag-and-drop PDF reports onto the audit list page, which automatically opens the create-audit dialog with the file attached. The dialog chains createAudit → uploadAuditReport mutations, with graceful handling for partial failures (audit created but upload failed).
Changes:
- AuditsPage: Add dropzone with visual overlay (dashed border + icon) when dragging PDFs
- CreateAuditDialog: Accept optional file prop, show file info, chain mutations on submit
- Extract audit ID from createAudit response to pass to uploadAuditReport
- Handle upload failure with warning toast, allowing manual upload from audit detail page
- Add react-dropzone dependency to console app
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Use the trust center's logo as favicon via the public file API URL,
falling back to the default favicon when no logo is configured.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Render dynamic file content into a bytes.Buffer first so that
renderer errors return 500 instead of a partial 200 response.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Implement server-side rendering of trust center page `<head>` with dynamic organization name and OG meta tags. Adds generic `FileRenderer` mechanism to statichandler for dynamic file rendering, allowing the trust server to inject templated content at request time.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Implements native Go 1.26 cross-origin protection to block state-changing cross-origin browser requests. Registers configured AllowedOrigins as trusted origins and wraps the API router to check all incoming requests. Non-browser clients (MCP, Slack webhooks) are unaffected as they lack the browser-only Sec-Fetch-Site header.
Signed-off-by: gearnode <gearnode@probo.inc>
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
- Hold SELECT FOR UPDATE lock within transaction by using tx directly instead of separate WithConn, ensuring mutual exclusion when multiple requests race to verify the same token
- Fix "resouce" → "resource" typo in authentication error messages
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Lima auto-forwards ports from the VM to localhost, which blocks local
development on the same ports (e.g. localhost:8080). Services are
already accessible via the VM IP shown by `sandbox.sh status`.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The provision script relied on LIMA_CIDATA_USER being set in the
environment, but during cloud-init this variable is not exported — it
only exists in /mnt/lima-cidata/lima.env. The file cannot be sourced
directly because values like LIMA_CIDATA_COMMENT contain unquoted
spaces, so we extract LIMA_CIDATA_USER with sed instead.
Also ensure HOME is set for root's go install commands and explicitly
set PATH when running make as the Lima user via su, since profile.d
scripts may not be loaded during cloud-init provisioning.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Fixes docker permissions by enabling Docker daemon during provisioning. Replaces make dev with three managed systemd services: probo-stack (auto-starting Docker Compose infra), probod (API server with gow for hot-reload), and probo-console (frontend dev server). The stack now starts automatically on VM boot; probod and console are started manually after build.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Adds a visibility enum to files with PRIVATE (default) and PUBLIC states.
PUBLIC files are accessible via an unauthenticated /api/files/v1/{fileID}
endpoint that redirects to a presigned S3 URL. Introduces pkg/file service
to manage file operations. Logo uploads (trust centers, organizations,
frameworks, references) are marked PUBLIC; other files are PRIVATE.
Includes database migration and backfill for existing logos.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
When PROBOD_ENCRYPTION_KEY is set, always run probod-bootstrap to regenerate
the config file. This ensures that updated environment variables take effect
even when a stale config file exists on a persistent volume (e.g., PVC).
Previously, an existing config file would be reused unconditionally, causing
env var changes to be ignored on container restart.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
URI schemes are case-insensitive per RFC 3986, so HTTP:// and HTTPS://
must also be recognized when checking for existing schemes.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The browse command and config loading were incorrectly prepending https:// to hosts that already had a scheme, resulting in malformed URLs like https://http://localhost:8080. Added normalizeHost() function to strip URL schemes when loading config, and added scheme detection in the browse command before prepending https://.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>