Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "proxy-smart-backend",
"displayName": "Proxy Smart Backend",
"version": "0.3.14-beta.202608131946.961ab42c4",
"version": "0.3.15-alpha.202608132031.ec7491a2e",
"type": "module",
"scripts": {
"test": "bun test --isolate",
Expand Down
18 changes: 18 additions & 0 deletions backend/src/lib/shl-scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,24 @@ interface FhirBundleLike {
[key: string]: unknown
}

/**
* True only when NOTHING narrows the share.
*
* The recipient is told "complete summary — the patient shared their full health
* record" on the strength of this, so every dimension that narrows a share has to
* be counted here. It lives beside the scope rules rather than inline at the mint
* site because that is how it drifted: it knew about selective de-selection and
* not about study scoping, so a single-study link claimed to carry everything
* while the proxy answered almost every query with 403 — which the viewer drew as
* "no allergies, no medications, no conditions".
*/
export function isCompleteShare(narrowing: {
selectiveScope?: unknown
studyInstanceUID?: string
}): boolean {
return !narrowing.selectiveScope && !narrowing.studyInstanceUID
}

/** True when the scope actually narrows anything (else all helpers are no-ops). */
export function isSelectiveScopeActive(scope: SelectiveScope): boolean {
return (
Expand Down
3 changes: 2 additions & 1 deletion backend/src/routes/api/shl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { shlSessionStore, type ShareScope, type ShlSession } from '@/lib/shl-ses
import {
isDicomPathAllowed,
scopeFhirRequest,
isCompleteShare,
isSelectiveScopeActive,
preScreenSelectiveRequest,
applySelectiveFilter,
Expand Down Expand Up @@ -449,7 +450,7 @@ export const shlRoutes = new Elysia({ prefix: '/shl', tags: ['shl'] })
scope: 'patient/*.read',
patient: patientId,
aud: `${config.baseUrl}/api/shl/fhir`,
complete: !shareScope,
complete: isCompleteShare({ selectiveScope: shareScope, studyInstanceUID: body.studyInstanceUID }),
})

// Generate SHL using kill-the-clipboard
Expand Down
28 changes: 28 additions & 0 deletions backend/test/shl-scope.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Max Health Inc.
// SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Commercial

/**
* SHL Scope Enforcement — unit tests for the pure access-control decisions.
*
Expand All @@ -10,6 +13,7 @@ import { describe, expect, it } from 'bun:test'
import {
isDicomPathAllowed,
scopeFhirRequest,
isCompleteShare,
isSelectiveScopeActive,
preScreenSelectiveRequest,
applySelectiveFilter,
Expand Down Expand Up @@ -286,3 +290,27 @@ describe('emptySearchBundle', () => {
expect(emptySearchBundle()).toEqual({ resourceType: 'Bundle', type: 'searchset', total: 0, entry: [] })
})
})

describe('isCompleteShare — what the recipient is told the link carries', () => {
it('is complete when nothing narrows the share', () => {
expect(isCompleteShare({})).toBe(true)
expect(isCompleteShare({ selectiveScope: undefined, studyInstanceUID: undefined })).toBe(true)
})

it('is NOT complete when the patient de-selected records', () => {
expect(isCompleteShare({ selectiveScope: { excludedTypes: ['Condition'] } })).toBe(false)
})

/**
* The regression: a study-scoped link reported `complete: true`, so the viewer
* showed "the patient shared their full health record" over a record whose every
* other query the proxy answers with 403.
*/
it('is NOT complete when the share is scoped to a single imaging study', () => {
expect(isCompleteShare({ studyInstanceUID: STUDY })).toBe(false)
})

it('is NOT complete when both narrowings apply', () => {
expect(isCompleteShare({ selectiveScope: { excludedTypes: ['Condition'] }, studyInstanceUID: STUDY })).toBe(false)
})
})
2 changes: 1 addition & 1 deletion config/eslint/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@proxy-smart/eslint-config",
"version": "0.3.14-beta.202608131924.cfdc0c7f7",
"version": "0.3.14-beta.202608131946.961ab42c4",
"private": true,
"type": "module",
"exports": {
Expand Down
2 changes: 1 addition & 1 deletion deploy/infra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "proxy-smart-infra",
"displayName": "Proxy Smart Infrastructure",
"description": "AWS CDK infrastructure for Proxy Smart production deployment",
"version": "0.3.14-beta.202608131946.961ab42c4",
"version": "0.3.15-alpha.202608132031.ec7491a2e",
"private": true,
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion frontend/smart-dicom-template/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "SMART DICOM Algorithm Template",
"description": "Starter kit for building SMART on FHIR imaging algorithm apps. Clone, implement your algorithm in src/algorithm.ts, and deploy as a SMART app.",
"private": true,
"version": "0.3.14-beta.202608131924.cfdc0c7f7",
"version": "0.3.14-beta.202608131946.961ab42c4",
"type": "module",
"scripts": {
"dev": "vite --port 5180",
Expand Down
2 changes: 1 addition & 1 deletion frontend/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "Proxy Smart Admin UI",
"description": "A web-based administration interface for managing healthcare applications and resources via Proxy Smart.",
"private": true,
"version": "0.3.14-beta.202608131924.cfdc0c7f7",
"version": "0.3.14-beta.202608131946.961ab42c4",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "proxy-smart",
"version": "0.3.14-beta.202608131946.961ab42c4",
"version": "0.3.15-alpha.202608132031.ec7491a2e",
"repository": {
"type": "git",
"url": "git+https://github.com/Max-Health-Inc/proxy-smart.git"
Expand Down
2 changes: 1 addition & 1 deletion packages/app-store/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@proxy-smart/app-store",
"version": "0.3.14-beta.202608131924.cfdc0c7f7",
"version": "0.3.14-beta.202608131946.961ab42c4",
"private": false,
"type": "module",
"description": "SMART on FHIR app store — manifest discovery, visibility configuration, and registry CRUD. Framework-agnostic.",
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@proxy-smart/auth",
"version": "0.3.14-beta.202608131924.cfdc0c7f7",
"version": "0.3.14-beta.202608131946.961ab42c4",
"private": false,
"type": "module",
"description": "SMART on FHIR STU 2.2.0 server-side authorization proxy — launch context, session management, token enrichment. Framework-agnostic, IdP-pluggable.",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@proxy-smart/cli",
"version": "0.3.14-beta.202608131924.cfdc0c7f7",
"version": "0.3.14-beta.202608131946.961ab42c4",
"private": false,
"type": "module",
"description": "Admin CLI for the proxy-smart SMART on FHIR authorization proxy. Authenticates via Keycloak OAuth (device flow or client_credentials) and drives the admin REST API.",
Expand Down
2 changes: 1 addition & 1 deletion packages/elysia-mcp/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@max-health-inc/elysia-mcp",
"version": "0.3.14-beta.202608131924.cfdc0c7f7",
"version": "0.3.14-beta.202608131946.961ab42c4",
"private": false,
"type": "module",
"description": "Auto-generate MCP tools and resources from Elysia routes via introspection. TypeBox-to-Standard-Schema bridge, Streamable HTTP transport, session management.",
Expand Down
2 changes: 1 addition & 1 deletion packages/patient-picker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "Proxy Smart Patient Picker",
"description": "Patient selection UI shown during SMART standalone launch when patient context is needed.",
"private": true,
"version": "0.3.14-beta.202608131924.cfdc0c7f7",
"version": "0.3.14-beta.202608131946.961ab42c4",
"type": "module",
"scripts": {
"dev": "vite --port 5176",
Expand Down
2 changes: 1 addition & 1 deletion scripts/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "proxy-smart-scripts",
"version": "0.3.14-beta.202608131946.961ab42c4",
"version": "0.3.15-alpha.202608132031.ec7491a2e",
"description": "CI/CD and development scripts for Proxy Smart",
"private": true,
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion testing/e2e/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@proxy-smart/e2e",
"version": "0.3.14-beta.202608131924.cfdc0c7f7",
"version": "0.3.14-beta.202608131946.961ab42c4",
"private": true,
"description": "End-to-end Playwright tests for Proxy Smart apps",
"scripts": {
Expand Down