Commit Graph

3738 Commits

Author SHA1 Message Date
Bryan Frimin
c2a1843c67 Address PR review comments
- 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>
2026-03-19 18:33:43 +01:00
Bryan Frimin
ad7a3ecbe9 Fix measure breadcrumb category filter
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>
2026-03-19 18:33:41 +01:00
Sacha Al Himdani
8ed6f1824f Fix unvalidated URL redirection in HTTP redirects
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>
2026-03-19 17:27:51 +01:00
Bryan Frimin
c74e5cc123 Add go fmt and go fix checks to lint
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>
2026-03-19 16:37:51 +01:00
Sacha Al Himdani
8b66a61990 Run go fix and go fmt
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-03-19 16:37:51 +01:00
Bryan Frimin
3f6c3c87f2 Release v0.146.1 2026-03-19 16:22:49 +01:00
Bryan Frimin
40df76d8b4 Fix SAML ACS endpoint CORS rejection
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>
2026-03-19 16:07:53 +01:00
dependabot[bot]
9120c2c662 Bump github.com/modelcontextprotocol/go-sdk from 1.4.0 to 1.4.1
Bumps [github.com/modelcontextprotocol/go-sdk](https://github.com/modelcontextprotocol/go-sdk) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/modelcontextprotocol/go-sdk/releases)
- [Commits](https://github.com/modelcontextprotocol/go-sdk/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: github.com/modelcontextprotocol/go-sdk
  dependency-version: 1.4.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 14:45:48 +01:00
Sacha Al Himdani
e1efc7b488 Release v0.146.0 2026-03-19 14:42:08 +01:00
Sacha Al Himdani
a5dc9a13ad Fix style
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-03-19 14:36:30 +01:00
Sacha Al Himdani
1db8e7133e Add document archiving
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>
2026-03-19 14:15:54 +01:00
Bryan Frimin
2e12c11c0c Fix flaky finding e2e tests: move advisory lock outside CTE
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>
2026-03-19 13:30:07 +01:00
Bryan Frimin
a6c88c9631 Add fulltext search to measures page
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-03-19 12:36:51 +01:00
Bryan Frimin
c3be391e9d Fix DocumentList: clear selection on filter change, update connection ID, fix indentation
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>
2026-03-19 12:31:41 +01:00
Bryan Frimin
4f54241382 Add document types filtering and rename ISMS to GOVERNANCE
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>
2026-03-19 12:31:39 +01:00
Bryan Frimin
2b08dea600 Fix measure count queries missing category column
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>
2026-03-19 12:25:34 +01:00
Bryan Frimin
d6b9280038 Fix owner deletion by qualifying ambiguous tenant_id column
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>
2026-03-19 12:14:19 +01:00
Bryan Frimin
922c0c2db8 Fix flaky e2e timestamp tests
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>
2026-03-19 11:48:13 +01:00
Bryan Frimin
b87ea87c42 Fix organization profile using owner's full name instead of org name
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>
2026-03-19 11:38:04 +01:00
Bryan Frimin
dd99eec4f6 Clamp pagination size in page.NewCursor
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>
2026-03-19 11:09:00 +01:00
Bryan Frimin
7227fd66f4 Release v0.145.0 2026-03-19 10:27:48 +01:00
Bryan Frimin
d7c120c144 Rename NONCONFORMITY to MINOR_NONCONFORMITY and add MAJOR_NONCONFORMITY
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>
2026-03-19 10:02:28 +01:00
Bryan Frimin
a46366fad4 Style
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-03-19 09:54:35 +01:00
Bryan Frimin
82f241628c Refactor MeasuresPage to use Relay fragments
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>
2026-03-19 09:54:33 +01:00
dependabot[bot]
69c418d1b7 Bump google.golang.org/grpc from 1.79.1 to 1.79.3
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.79.1 to 1.79.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.79.1...v1.79.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.79.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 09:45:55 +01:00
Bryan Frimin
32cc4a4f9c WIP
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-03-19 09:39:39 +01:00
Bryan Frimin
239f201a93 Restore missing useState import in AuditsPage
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-03-19 09:39:38 +01:00
Bryan Frimin
5c42d2e9ba Simplify dropzone overlay to use fixed positioning without portal
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>
2026-03-19 09:39:38 +01:00
Bryan Frimin
6eebfa11ef Use state instead of ref for main element portal target
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>
2026-03-19 09:39:38 +01:00
Bryan Frimin
f89fdc02b1 Clean up main element style on unmount in AuditsPage
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>
2026-03-19 09:39:37 +01:00
Bryan Frimin
44cc650af2 Fix dragCounterRef going negative in audit list dropzone
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>
2026-03-19 09:39:37 +01:00
Bryan Frimin
e055b21bc3 Exclude header from audit dropzone overlay
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>
2026-03-19 09:39:37 +01:00
Bryan Frimin
d746f32903 Use window-level drag detection for audit list dropzone
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>
2026-03-19 09:39:37 +01:00
Bryan Frimin
d360725d24 Fix import order and handle GraphQL errors in upload onCompleted
- 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>
2026-03-19 09:39:36 +01:00
Bryan Frimin
65e3bd014b Fix ESLint violations in CreateAuditDialog
- 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>
2026-03-19 09:39:36 +01:00
Bryan Frimin
a6d5e9b1f0 Colocate upload mutation and remove promisifyMutation usage
Move uploadAuditReportMutation into CreateAuditDialog and replace
promisifyMutation with a Promise wrapper using native Relay callbacks.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-03-19 09:39:36 +01:00
Bryan Frimin
b4a6618736 Add PDF dropzone to audit list for streamlined report upload
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>
2026-03-19 09:39:34 +01:00
Bryan Frimin
1e5f4012f0 Add dynamic favicon for trust center using file API
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>
2026-03-19 09:32:37 +01:00
Bryan Frimin
f01b28aad9 Buffer renderer output before sending HTTP 200
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>
2026-03-19 09:32:36 +01:00
Bryan Frimin
751d9eb1f3 Add SSR for compliance page with dynamic title and meta tags
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>
2026-03-19 09:32:34 +01:00
Bryan Frimin
ea21f85887 Add http.CrossOriginProtection for CSRF defense using Sec-Fetch-Site headers
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>
2026-03-19 09:31:52 +01:00
Bryan Frimin
888838cfb0 Fix race condition in magic link token verification and typo in auth error message
- 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>
2026-03-19 09:31:15 +01:00
dependabot[bot]
12cfbd3f5b Bump github.com/russellhaering/goxmldsig from 1.5.0 to 1.6.0
Bumps [github.com/russellhaering/goxmldsig](https://github.com/russellhaering/goxmldsig) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/russellhaering/goxmldsig/releases)
- [Commits](https://github.com/russellhaering/goxmldsig/compare/v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/russellhaering/goxmldsig
  dependency-version: 1.6.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 20:58:40 +00:00
Bryan Frimin
2be7337344 Disable Lima automatic port forwarding for sandbox
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>
2026-03-18 20:03:36 +01:00
Bryan Frimin
f3aa0dea36 Fix sandbox provisioning failure due to missing LIMA_CIDATA_USER
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>
2026-03-18 19:51:53 +01:00
Bryan Frimin
9fd3473147 Fix sandbox Docker root issue and replace make dev with systemd services
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>
2026-03-18 19:51:53 +01:00
Bryan Frimin
a5743729f7 Add file visibility (PRIVATE/PUBLIC) + public files API
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>
2026-03-18 19:19:04 +01:00
Bryan Frimin
9237ad8ce2 Fix entrypoint to regenerate config when env vars are updated
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>
2026-03-18 19:12:06 +01:00
Bryan Frimin
042fd29e93 Handle case-insensitive URI schemes in host normalization
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>
2026-03-18 17:28:05 +01:00
Bryan Frimin
0f1fc2f069 Fix CLI URL scheme handling when using http:// addresses
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>
2026-03-18 17:19:44 +01:00