Bump the Go toolchain from 1.26.4 to 1.26.5 to address CVE-2026-42505
(ECH handshake de-anonymization) and CVE-2026-39822 (os.Root symlink
following on Unix).
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Any self-provisioned trust center visitor could accept another
visitor's NDA signature or inject audit-trail events into it by
supplying its GID, since AcceptSignature and RecordEvent trusted the
client-supplied signature ID without checking it belonged to the
caller (GHSA-22xj-f767-ppw6). SignerEmail/ActorEmail are always
derived from the verified session identity, never client input, so
comparing them against the signature's stored SignerEmail in
pkg/esign/service.go closes the hole at its root without touching the
resolver-level authorization already in place elsewhere.
Adds an e2e regression test that self-provisions two trust center
visitors through the real magic-link flow and confirms one cannot
touch the other's signature.
Signed-off-by: Bryan Frimin <bryan@probo.com>
Drop the redundant embedded Resolver selector flagged by staticcheck
(QF1008) in the membership and organization permission resolvers, and
add the whitespace wsl_v5 expects around the attribute range loop and
the final return in permission.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Replace the deny-based restriction on granting OWNER with role-scoped
allow policies so authorization fails closed: admins may create and
update memberships only when the assigned role is not OWNER, and the
absence of a target role no longer implies permission.
To keep console UI gating accurate without loosening the base grants,
the permission field gains an optional typed options argument
(PermissionOptionsInput) that forwards target_role into the dry-run
authorization. Only the two role-related console calls (create user,
update membership) pass it; the OWNER option stays hidden for admins via
the existing assignable-roles helper.
Add a non-regression test that an admin cannot promote a member to OWNER
while still being able to change members between non-owner roles.
An organization ADMIN could hard-remove members, including OWNERs, because
removeUser (connect and MCP) only checked the weaker iam:membership-profile:delete
gate. Authorize the owner-only iam:membership:delete instead, and expose the
source attribute on MembershipProfile so the owner grant's non-SCIM condition
can match.
Consolidate ownership-grant authorization into policy for both createUser and
updateMembership: each resolver passes the requested role as a target_role
attribute and ADMIN is denied granting ownership via deny-create-owner /
deny-promote-owner. target_role is distinct from resource.role, which is the
target's current role and guards editing existing owners. With no callers left,
the iam:membership-role:set-owner action (grant and OAuth2 scope) is removed.
Also pass the authorized scope through to the RemoveUser/CreateUser services,
gate the console Remove action on iam:membership:delete, and add regression
tests plus a changelog entry.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Add FERPA and PCI DSS framework datasets (controls plus light/dark
logos), their logo components, and register both in the framework
selector so they can be imported like the existing frameworks.
Co-authored-by: Antoine Bouchardy <antoine@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
The third-party vetting agent runs a suite of HTTP "security" tools on
the internal worker network against a caller-supplied URL that is only
validated for length and charset, not host. Several tools reached
internal, loopback, and link-local addresses:
- analyze_csp used a bare http.Client with no host validation, no
redirect control, and no rebinding-safe transport, reflecting the
target's CSP header back to the caller.
- check_security_headers, fetch_robots_txt, and fetch_sitemap
validated only the initial host, then followed 3xx redirects with an
ordinary client, yielding full-read SSRF via a redirect to an
internal address.
- check_cors validated the URL but still dialed through an ordinary
transport, leaving it exposed to DNS-rebinding TOCTOU.
Route every one of these clients through the house-standard
httpclient.DefaultPooledClient(WithSSRFProtection()), which rejects
dials to loopback, private, CGNAT, link-local, ULA, IPv4-mapped, and
reserved ranges on the resolved peer IP at connect time (defeating DNS
rebinding on every redirect hop) and refuses cross-origin redirects.
download_pdf moves onto the same client, and the now-unused local
netcheck.NewPinnedTransport is removed. analyze_csp also gains an
up-front ValidatePublicURL check for a clean early error and scheme
enforcement.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
The DocumentVersionSignatureFilter exposed a field named `state` that
actually filters on the signatory's profile state, which was ambiguous
next to the signature `states` field. Rename it to `profileState`
(GraphQL) / `profile_state` (MCP) across the schema, spec, resolvers,
console app, and n8n node for clarity.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Guard the content field against empty strings so adding it in the UI
without a value no longer sends content: "" to the API, which is not
valid ProseMirror content. This matches the existing defaultApproverIds
guard.
Also expose title, classification, and document type on the update
operation. Like content, these edit the current draft version, creating
one from the latest published version when none exists.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Remove the createDraftVersion document operation from the n8n node and
instead expose the document body on the update operation. Updating the
body edits the current draft version, or creates one from the latest
published version when none exists, so a dedicated draft-creation
operation is redundant.
Also correct the content field help text across the document
operations: the API expects a ProseMirror document JSON string, not
markdown.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Normalize Anthropic thinking budgets while building message parameters so budget_tokens stays below max_tokens with response headroom. When the configured max token budget is too small to support Anthropic's minimum thinking budget, omit thinking for that request instead of sending an invalid payload.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Sacha Al Himdani <SachaProbo@users.noreply.github.com>
The public trust API derived its authorization scope from client-supplied
global IDs, so a visitor on one trust center could resolve nodes, export
audit-report PDFs, and read or mutate electronic signatures belonging to
another organization (cross-tenant access).
Every trust API resolver now derives its scope from the active compliance
page's organization via compliancepage.ScopeFromContext, so reads are always
confined to the page's tenant. Cross-tenant or unknown IDs surface as
not-found instead of leaking data or returning a 500. Active/presence is
enforced upstream by the id and presence middlewares.
esign's signature operations (GetSignatureByID, AcceptSignature, RecordEvent)
now take a caller-provided scope instead of deriving one from the requested
ID, so signature reads and mutations are tenant-scoped at the source. This
removes the need for a resolver-level authorization helper; RecordEvent also
verifies signature ownership within scope before recording, since the event
foreign key is not tenant-composite.
Adds e2e non-regression tests covering owning vs. foreign trust center report
export and the generic node(id:) resolver.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
The available document access list backing Grant All included documents
whose trust center visibility is PRIVATE but that have no published
version. Require a published major so the grantable set matches the
request-all filter and the Slack notification, which already skip drafts
and hidden documents.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
The trust center access-request Slack notification listed every document
referenced by a document access, including drafts with no published
version and documents whose trust center visibility is NONE. Skip those
so the message only surfaces documents a requester could actually be
granted.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Several HTTP entry points still parsed RemoteAddr directly, so behind
a layer-7 proxy they recorded the load balancer IP instead of the
signer's. Route NDA acceptance, signing events, document sign/approve,
and session updates through clientip.Extract, which honors Forwarded
and X-Forwarded-For when trustedproxy allows them.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
An organization ADMIN could mint an OWNER membership via createUser, which
only gated iam:membership-profile:create and bypassed the owner-only
iam:membership-role:set-owner check that updateMembership already enforces.
Gate the requested role in both createUser entry points (connect resolver
and the MCP CreateUserTool) with an additional set-owner authorization when
the role is OWNER, mirroring updateMembership. Add a regression test that
locks the ADMIN/OWNER privilege boundary the fix relies on.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Document that comments should be rare and short, reserved for genuinely
surprising behavior or context that cannot live in the code.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Add SECURITY_NOTES.md entries for GHSA-c74x-79w6-63jh (cross-tenant
IDOR via unvalidated Finding->Risk and ProcessingActivity->DPO
references) and GHSA-prh2-g8pv-m7p9 (GraphQL alias-flooding DoS),
ahead of public disclosure.
Signed-off-by: Bryan Frimin <bryan@probo.com>
The CLI still defaulted to *.console.getprobo.com for region
selection, examples, and the non-interactive fallback host. Align
those with the current SaaS hostnames so new logins store the right
config keys.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
The n8n marketplace review flagged the codex node field as using
the wrong identifier format. Update it to match the scoped package
name and the probo node name declared in Probo.node.ts.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Marketplace review flagged Developer Tools and Automation as
unsupported n8n codex category values. Keep Development and use
Utility per review guidance.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
The Probo node implements execute() directly, so n8n never applies
requestDefaults from the node description. Drop the dead block flagged
in marketplace review.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Replace the legacy console.getprobo.com hostnames in the n8n README
and credential default with the current regional Probo URLs.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
The n8n marketplace review requires a README in packages/n8n-node.
Document installation, Probo API credentials, and workflow examples
so users can configure the community node without reading the source.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
The OAuth token and consent UIs fall back to the raw scope string when
a label is missing from scopeLabels. Add read and manage labels for
v1:resource-alias so it matches other API scopes.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Signed-off-by: Sacha Al Himdani <sacha@probo.com>