clawd/memory/shannon-scan-2026-02-14/deliverables/recon_deliverable.md

60 KiB

Reconnaissance Deliverable: Inou Health Portal Attack Surface Analysis

0) HOW TO READ THIS

This reconnaissance report provides a comprehensive map of the application's attack surface, with special emphasis on authorization and privilege escalation opportunities for the Authorization Analysis Specialist.

Key Sections for Authorization Analysis:

  • Section 4 (API Endpoint Inventory): Contains authorization details for each endpoint - focus on "Required Role" and "Object ID Parameters" columns to identify IDOR candidates.
  • Section 6.4 (Guards Directory): Catalog of authorization controls - understand what each guard means before analyzing vulnerabilities.
  • Section 7 (Role & Privilege Architecture): Complete role hierarchy and privilege mapping - use this to understand the privilege lattice and identify escalation targets.
  • Section 8 (Authorization Vulnerability Candidates): Pre-prioritized lists of endpoints for horizontal, vertical, and context-based authorization testing.

How to Use the Network Mapping (Section 6): The entity/flow mapping shows system boundaries and data sensitivity levels. Pay special attention to flows marked with authorization guards and entities handling PII/sensitive data.

Priority Order for Testing: Start with Section 8's High-priority horizontal candidates, then vertical escalation endpoints for each role level, finally context-based workflow bypasses.

1. Executive Summary

Inou Health is a health data management platform that enables users to securely organize and share their medical records with AI assistants. The application uses a Go-based backend with custom frameworks, passwordless email authentication, and a permission-based authorization model.

Core Technology Stack:

  • Frontend: Server-side rendered HTML templates (Go html/template)
  • Backend: Go (Golang) with standard library net/http
  • Infrastructure: Self-hosted on Linux (/tank/inou directory structure)
  • Database: SQLite with AES-256-GCM encryption at rest
  • Authentication: Custom passwordless email (6-digit verification codes)
  • Authorization: Custom RBAC with bitmask permissions (read/write/delete/manage)

Primary User-Facing Components:

  • Public marketing pages (/, /pricing, /demo)
  • Passwordless authentication flow (/start, /send-code, /verify)
  • Dossier (health record) management dashboard
  • File upload system for medical imaging, lab results, genetics data
  • OAuth 2.0 server for third-party AI integrations (Claude Desktop, ChatGPT)
  • Model Context Protocol (MCP) server for AI assistant integrations
  • RESTful JSON API (v1) for mobile/programmatic access

Attack Surface Scope: Network-accessible web application endpoints only. Local CLI tools, build scripts, and development utilities are out of scope.

2. Technology & Service Map

Frontend

  • Framework: Server-side rendered Go templates (html/template package)
  • JavaScript: Minimal vanilla JS for UI interactions
  • Authentication UI: Custom passwordless login forms
  • Key Libraries: None identified (vanilla HTML/CSS/JS)

Backend

  • Language: Go (Golang) 1.x
  • Framework: Standard library net/http with http.ServeMux router
  • Key Dependencies:
    • golang.org/x/crypto - AES-256-GCM encryption, bcrypt
    • modernc.org/sqlite - Pure Go SQLite implementation
    • Standard library only (minimal external dependencies)
  • DICOM Processing: External binary at /tank/inou/bin/import-dicom
  • LLM Integration: Google Gemini API for medical data parsing

Infrastructure

  • Hosting: Self-hosted Linux server
  • Web Server: Go net/http (ports 8443 for portal, 8082 for API)
  • Database: SQLite (inou.db for main data, auth.db for OAuth)
  • File Storage: Local filesystem at /tank/inou/uploads/ (encrypted)
  • Encryption: FIPS 140-3 compliant AES-256-GCM with master key at /tank/inou/master.key
  • DICOM Viewer: Separate service on localhost:8765 (Orthanc or similar)

Identified Subdomains

  • Primary Domain: inou.com
  • No subdomains discovered - single-domain application

Open Ports & Services

  • Port 8443 (HTTPS): Main portal web server (public-facing)
  • Port 8082 (HTTP): Internal API server (localhost only, proxied by portal)
  • Port 8765 (HTTP): DICOM viewer service (localhost only, proxied by portal)

Note: External port scanning not performed per scope. Information derived from source code analysis.

3. Authentication & Session Management Flow

Entry Points

  • GET /start - Passwordless login page (email entry)
  • POST /send-code - Sends 6-digit verification code to email
  • POST /verify - Validates verification code and creates session
  • GET /oauth/authorize - OAuth 2.0 authorization endpoint for third-party apps
  • POST /oauth/token - OAuth 2.0 token exchange endpoint

Mechanism - Step-by-Step Process

Web Portal Authentication Flow

  1. Email Submission (POST /send-code)

    • User enters email at /start
    • Bot detection via nonce parameter (must be >= 2000ms)
    • Email normalized: strings.ToLower(strings.TrimSpace(email))
    • 6-digit verification code generated via crypto/rand
    • Code stored encrypted in database with 10-minute expiry
    • Code sent via email (SMTP)
    • Code Location: /repos/inou-portal/portal/main.go:540-557
  2. Code Verification (POST /verify)

    • User submits email + 6-digit code
    • Code validated against database (single-use)
    • Backdoor code 250365 exists for testing (SECURITY NOTE)
    • Code Location: /repos/inou-portal/portal/main.go:560-590, /repos/inou-portal/lib/dbcore.go:330-353
  3. Session Creation

    • 30-day persistent cookie named login created
    • Cookie value: plain dossier ID (16-char hex)
    • Cookie attributes: HttpOnly=true, Secure=true, SameSite=Lax
    • No server-side session expiration tracking
    • Code Location: /repos/inou-portal/portal/main.go:311-313
  4. Profile Completeness Check

    • Redirect to /onboard if name/DOB/sex not set
    • Redirect to /dashboard if profile complete
    • Code Location: /repos/inou-portal/portal/main.go:582-589

Mobile API Authentication Flow

  1. Code Request (POST /api/v1/auth/send)

    • JSON API endpoint for mobile clients
    • Generates 6-digit code with 10-minute expiry
    • Stores code in Dossier.AuthCode field
    • Returns success/failure JSON
    • Code Location: /repos/inou-portal/portal/api_mobile.go:55-103
  2. Token Generation (POST /api/v1/auth/verify)

    • Validates 6-digit code (same backdoor exists)
    • Generates or reuses 64-char hex session token
    • Session token stored in Dossier.SessionToken field (never expires)
    • Returns {"token": "...", "needs_onboard": true/false}
    • Code Location: /repos/inou-portal/portal/api_mobile.go:105-149

OAuth 2.0 Flow (for AI Integrations)

  1. Authorization Request (GET /oauth/authorize)

    • Supports Authorization Code flow with PKCE (S256)
    • Parameters: client_id, redirect_uri, code_challenge, state
    • Validates client_id and redirect_uri against whitelist
    • Redirects to /start if user not logged in
    • Generates 10-minute authorization code (64-char hex)
    • Code Location: /repos/inou-portal/portal/oauth.go:52-140
  2. Token Exchange (POST /oauth/token)

    • Grant types: authorization_code, refresh_token
    • PKCE validation: SHA256(code_verifier) == code_challenge
    • Returns encrypted access token (15 minutes) + refresh token (30 days)
    • Access token format: AES-encrypted JSON {d: dossierID, exp: timestamp}
    • Refresh token: 64-char hex stored in auth.db (PLAINTEXT)
    • Code Location: /repos/inou-portal/portal/oauth.go:144-296

Code Pointers

Authentication Logic:

  • Main login handler: /repos/inou-portal/portal/main.go:658-662 (handleLogin)
  • Send code: /repos/inou-portal/portal/main.go:540-557 (handleSendCode)
  • Verify code: /repos/inou-portal/portal/main.go:560-590 (handleVerify)
  • Code generation: /repos/inou-portal/lib/dbcore.go:291-327 (DossierLogin)
  • Code validation: /repos/inou-portal/lib/dbcore.go:330-353 (DossierVerify)

Session Management:

  • Cookie creation: /repos/inou-portal/portal/main.go:311-313 (setLoginCookie)
  • Session validation: /repos/inou-portal/portal/main.go:279-285 (getLoggedInDossier)
  • Session token lookup: /repos/inou-portal/lib/dbcore.go:356-364 (DossierGetBySessionToken)

OAuth Implementation:

  • Authorization endpoint: /repos/inou-portal/portal/oauth.go:52-140
  • Token endpoint: /repos/inou-portal/portal/oauth.go:144-296
  • Token creation: /repos/inou-portal/lib/crypto.go:174-182 (TokenCreate)
  • Token validation: /repos/inou-portal/lib/crypto.go:186-207 (TokenParse)
  • PKCE verification: /repos/inou-portal/lib/db_queries.go:861-875

3.1 Role Assignment Process

Role Determination: This application does NOT use traditional roles. Instead, it uses a permission-based system with bitmask capabilities.

Permission Assignment on Authentication:

  • New users get implicit owner permissions on their own dossier (self-access)
  • Self-access check: if accessorID == dossierID { return true } (automatic, no database record)
  • Code Location: /repos/inou-portal/lib/rbac.go:23-25

Permission Grant Process:

  • Users with PermManage (bit 8) can grant permissions to others
  • Permissions granted via /dossier/{id}/permissions page
  • Permission templates available: parent/guardian (read+write+manage), caregiver/medical (read+write), custom
  • Templates converted to bitmask at grant time
  • Code Location: /repos/inou-portal/portal/main.go:1376-1464

Default Permissions:

  • Self (owner): All permissions (implicit, not stored)
  • Newly granted access: Depends on role template selected by grantor

Permission Upgrade Path:

  • Only users with PermManage can grant/modify permissions
  • No self-service upgrade mechanism
  • No automatic privilege escalation

3.2 Privilege Storage & Validation

Storage Location:

  • Database Table: access table in inou.db
  • Schema: {AccessID, DossierID, GranteeID, EntryID, Relation, Ops, CreatedAt}
  • Ops Field: Integer bitmask (1=read, 2=write, 4=delete, 8=manage)
  • Encryption: Access records encrypted at rest via AES-256-GCM
  • Code Location: /repos/inou-portal/lib/types.go:189-197

Alternative Storage (for authentication only):

  • Session Cookies: Contain dossier ID only (no permissions stored)
  • OAuth Access Tokens: Encrypted JSON with {d: dossierID, exp: timestamp} (no permissions)
  • Mobile Session Tokens: Random 64-char hex (lookup dossier ID only)

Validation Points:

  • API Middleware: requireDossierAccess(), requireEntryAccess(), requireManageAccess()
    • Location: /repos/inou-portal/api/auth.go:87-131
  • V1 API: v1CanAccess(), v1CanWrite()
    • Location: /repos/inou-portal/api/api_v1.go:61-86
  • Core RBAC: CheckAccess(accessorID, dossierID, entryID, perm)
    • Location: /repos/inou-portal/lib/rbac.go:19-54

Validation Process:

  1. Extract dossier ID from cookie/token/header
  2. Query access table for grants where GranteeID = accessorID AND DossierID = targetDossierID
  3. Check if (grant.Ops & requestedPermission) != 0
  4. Special cases: self-access (owner), SystemContext, empty accessorID bypass checks

Cache/Session Persistence:

  • No caching: Permissions checked on every request via database query
  • Session Duration:
    • Portal cookies: 30 days
    • Mobile session tokens: Never expire
    • OAuth access tokens: 15 minutes (then must refresh)

3.3 Role Switching & Impersonation

Impersonation Features: NONE - Not implemented

Role Switching: NONE - Not implemented

Privilege Escalation Mechanisms: NONE - No "sudo mode" or temporary elevation

Audit Trail:

  • Audit log exists at /dossier/{id}/audit showing access grants/revokes
  • No logging of impersonation (feature doesn't exist)
  • Code Location: /repos/inou-portal/portal/main.go:1262-1278 (handleAuditLog)

System Context Bypass:

  • SystemAccessorID: Hardcoded value 7b3a3ee1c2776dcd bypasses all RBAC
  • Empty AccessorID: Empty string "" also bypasses all checks
  • Localhost Bypass: Requests from 127.0.0.1 or ::1 get SystemContext (full access)
  • Code Locations:
    • SystemAccessorID: /repos/inou-portal/lib/config.go:27
    • Localhost bypass: /repos/inou-portal/api/auth.go:151-156
    • Empty accessor bypass: /repos/inou-portal/lib/rbac.go:20-22

4. API Endpoint Inventory

Network Surface Focus: Only includes API endpoints accessible through the target web application. Excludes development/debug endpoints, local-only utilities, and components unreachable via network requests.

Portal Server Endpoints (Port 8443)

Method Endpoint Path Required Role Object ID Parameters Authorization Mechanism Description & Code Pointer
GET/POST / anon None None Public landing page. /repos/inou-portal/portal/main.go:516
POST /send-code anon None Bot detection (nonce) Sends 6-digit verification code. /repos/inou-portal/portal/main.go:540
POST /verify anon None None Validates verification code, creates session. /repos/inou-portal/portal/main.go:560
GET/POST /onboard user None Cookie login required Profile completion (name/DOB/sex). /repos/inou-portal/portal/main.go:593
GET /logout anon None None Clears login cookie. /repos/inou-portal/portal/main.go:638
POST /set-lang anon None None Sets language preference. /repos/inou-portal/portal/main.go:643
GET /start anon None None Login page. /repos/inou-portal/portal/main.go:658
GET /dashboard user None Cookie login User dashboard showing all dossiers. /repos/inou-portal/portal/main.go:831
GET /connect anon None None AI integration setup instructions. /repos/inou-portal/portal/main.go:664
GET/POST /invite user None Cookie login Invite management page. /repos/inou-portal/portal/main.go:795
GET /demo anon None None Demo dossier (ID: 1111111111111111). /repos/inou-portal/portal/main.go:867
GET /pricing anon None None Pricing tiers page. /repos/inou-portal/portal/main.go:694
GET /privacy-policy anon None None Privacy policy. /repos/inou-portal/portal/main.go:689
GET /legal/terms anon None None Terms of service. /repos/inou-portal/portal/main.go:709
GET /legal/dpa anon None None Data Processing Agreement. /repos/inou-portal/portal/main.go:704
GET/POST /dossier/add user None Cookie login Create new dossier. /repos/inou-portal/portal/main.go:906
GET /dossier/{id} user id (dossierID) PermRead on dossier View dossier details. /repos/inou-portal/portal/dossier_sections.go:678
GET/POST /dossier/{id}/edit user id PermWrite on dossier Edit dossier profile. /repos/inou-portal/portal/main.go:977
GET/POST /dossier/{id}/share user id PermManage on dossier Share access via email invitation. /repos/inou-portal/portal/main.go:1193
POST /dossier/{id}/revoke user id, granteeId (POST param) PermManage on dossier Revoke access from user. /repos/inou-portal/portal/main.go:1326
GET /dossier/{id}/audit user id PermRead on dossier View access audit log. /repos/inou-portal/portal/main.go:1262
GET /dossier/{id}/export user id PermRead on dossier Download dossier data as JSON. /repos/inou-portal/portal/main.go:1100
GET/POST /dossier/{id}/permissions user id PermManage on dossier Manage permissions (grant/revoke access). /repos/inou-portal/portal/main.go:1376
GET/POST /dossier/{id}/rbac/{role} user id, role (path param) PermManage on dossier Edit role-based permissions. /repos/inou-portal/portal/main.go:1531
GET/POST /dossier/{id}/access/{granteeId} user id, granteeId PermManage on dossier Edit specific access grant. /repos/inou-portal/portal/main.go:1669
GET /dossier/{id}/trackers user id PermRead on dossier Daily check-in trackers (vitals, medications, etc.). /repos/inou-portal/portal/trackers.go:78
GET /dossier/{id}/trackers/card/{trackerId} user id, trackerId PermRead Render tracker card HTML. /repos/inou-portal/portal/trackers.go:473
POST /dossier/{id}/trackers/respond user id, tracker_id (POST) PermWrite Submit tracker response. /repos/inou-portal/portal/trackers.go:348
GET /dossier/{id}/upload user id PermRead File upload page. /repos/inou-portal/portal/upload.go:117
POST /dossier/{id}/upload user id PermWrite Upload medical files (imaging, labs, genetics). /repos/inou-portal/portal/upload.go:153
DELETE /dossier/{id}/files/{fileId}/delete user id, fileId PermDelete Delete uploaded file. /repos/inou-portal/portal/upload.go:261
POST /dossier/{id}/files/{fileId}/update user id, fileId PermWrite Update file metadata. /repos/inou-portal/portal/upload.go:306
GET /dossier/{id}/files/{fileId}/status user id, fileId PermRead Check file processing status. /repos/inou-portal/portal/main.go:1970
POST /dossier/{id}/process-imaging user id PermWrite Process uploaded DICOM imaging files. /repos/inou-portal/portal/upload.go:364
GET /image/{id} user id (entryID) PermRead on entry Retrieve medical image (proxied to API). /repos/inou-portal/portal/api_proxy.go:57
GET /contact-sheet.webp/{id} user id (studyID) PermRead on entry Contact sheet preview image. /repos/inou-portal/portal/api_proxy.go:57
GET /api anon None None API documentation page. /repos/inou-portal/portal/main.go:717
POST /api/token/generate user None Cookie login Generate long-lived API token. /repos/inou-portal/portal/main.go:726
POST /api/token/regenerate user None Cookie login Regenerate API token (invalidates old). /repos/inou-portal/portal/main.go:747
GET /api/openapi.yaml anon None None OpenAPI specification. /repos/inou-portal/portal/main.go:768
GET /api/docs anon None None API documentation (Swagger). /repos/inou-portal/portal/main.go:773

Mobile API Endpoints (Portal Server)

Method Endpoint Path Required Role Object ID Parameters Authorization Mechanism Description & Code Pointer
POST /api/v1/auth/send anon None None Mobile: Send verification code. /repos/inou-portal/portal/api_mobile.go:55
POST /api/v1/auth/verify anon None None Mobile: Verify code, get session token. /repos/inou-portal/portal/api_mobile.go:105
GET /api/v1/dashboard user None Bearer token Mobile: Dashboard data (all dossiers). /repos/inou-portal/portal/api_mobile.go:165
GET /api/v1/trackers user dossier (query) Bearer token Mobile: Get trackers for dossier. /repos/inou-portal/portal/api_mobile.go:209
POST /api/v1/trackers/respond user dossier (query) Bearer token Mobile: Submit tracker response. /repos/inou-portal/portal/api_mobile.go:220

OAuth 2.0 Endpoints

Method Endpoint Path Required Role Object ID Parameters Authorization Mechanism Description & Code Pointer
GET /oauth/authorize user None Cookie login (redirects if missing) OAuth authorization endpoint (PKCE supported). /repos/inou-portal/portal/oauth.go:52
POST /oauth/token anon None Client credentials Token exchange (authorization_code, refresh_token grants). /repos/inou-portal/portal/oauth.go:144
GET /oauth/userinfo user None Bearer token UserInfo endpoint (OpenID Connect compatible). /repos/inou-portal/portal/oauth.go:298
POST /oauth/revoke anon None Client credentials Revoke refresh token. /repos/inou-portal/portal/oauth.go:337

MCP (Model Context Protocol) Endpoints

Method Endpoint Path Required Role Object ID Parameters Authorization Mechanism Description & Code Pointer
GET /.well-known/oauth-protected-resource anon None None OAuth resource metadata. /repos/inou-portal/portal/mcp_http.go:26
GET /.well-known/oauth-authorization-server anon None None OAuth server metadata. /repos/inou-portal/portal/mcp_http.go:54
POST /register anon None None Dynamic client registration (OAuth). /repos/inou-portal/portal/mcp_http.go:88
POST /mcp user None Bearer token MCP JSON-RPC endpoint (AI tools/prompts). /repos/inou-portal/portal/mcp_http.go:178

API Server Endpoints (Port 8082 - Proxied via Portal)

Method Endpoint Path Required Role Object ID Parameters Authorization Mechanism Description & Code Pointer
GET /api/version anon None None API version info. /repos/inou-portal/api/api_version.go:8
GET /api/dossiers user None Bearer/session token List accessible dossiers. /repos/inou-portal/api/api_dossiers.go:10
GET/POST /api/dossier user/system dossier (query) Bearer/session/localhost Get/create dossier. /repos/inou-portal/api/api_dossier.go:29
GET /api/studies user dossier (query) Bearer/session + PermRead List medical imaging studies. /repos/inou-portal/api/api_studies.go:10
GET /api/series user dossier, study (query) Bearer/session + PermRead List imaging series. /repos/inou-portal/api/api_series.go:12
GET /api/slices user dossier, series (query) Bearer/session + PermRead List image slices. /repos/inou-portal/api/api_slices.go:10
GET /image/{id} user id (entryID) PermRead on entry Retrieve medical image file. /repos/inou-portal/api/api_image.go:57
GET /contact-sheet.webp/{id} user id (studyID) PermRead on entry Generate contact sheet image. /repos/inou-portal/api/api_contact_sheet.go:61
GET /api/labs/tests user dossier (query) PermRead List lab test types. /repos/inou-portal/api/api_labs.go:11
GET /api/labs/results user dossier (query) PermRead Get lab results. /repos/inou-portal/api/api_labs.go:39
GET /api/access system None Localhost only List access grants (internal). /repos/inou-portal/api/api_access.go:49
POST /api/access system None Localhost only Grant/revoke access (internal). /repos/inou-portal/api/api_access.go:101
GET/POST /api/audit user dossier (query) PermRead Get audit log entries. /repos/inou-portal/api/api_audit.go:25
GET /api/entries user dossier (query) PermRead List entries (medical records). /repos/inou-portal/api/api_entries.go:45
POST /api/entries user/system None Bearer/session/localhost + PermWrite Create/update entries. /repos/inou-portal/api/api_entries.go:45
GET/POST /api/trackers user dossier (query) Bearer/session Get/update trackers. /repos/inou-portal/api/api_trackers.go:177
POST /api/trackers/freeform user dossier (query) Bearer/session + PermWrite Submit freeform tracker entry. /repos/inou-portal/api/api_trackers.go:319

V1 RESTful API Endpoints (API Server)

Method Endpoint Path Required Role Object ID Parameters Authorization Mechanism Description & Code Pointer
GET /api/v1/health anon None None Health check endpoint. /repos/inou-portal/api/api_v1.go:702
POST /api/v1/token user None Bearer token Generate new access token. /repos/inou-portal/api/api_v1.go:105
GET /api/v1/dossiers user None Bearer token List accessible dossiers. /repos/inou-portal/api/api_v1.go:136
GET /api/v1/dossiers/{id} user id Bearer token + PermRead Get dossier details. /repos/inou-portal/api/api_v1.go:189
GET /api/v1/dossiers/{id}/entries user id Bearer token + PermRead List entries in dossier. /repos/inou-portal/api/api_v1.go:212
GET /api/v1/dossiers/{id}/entries/{entryId} user id, entryId Bearer token + PermRead Get specific entry. /repos/inou-portal/api/api_v1.go:277
GET /api/v1/dossiers/{id}/access user id Bearer token + PermRead List access grants for dossier. /repos/inou-portal/api/api_v1.go:335
GET /api/v1/dossiers/{id}/audit user id Bearer token + PermRead Get audit log. /repos/inou-portal/api/api_v1.go:365
GET /api/v1/dossiers/{id}/trackers user id Bearer token + PermRead Get trackers. /repos/inou-portal/api/api_v1.go:563
GET /api/v1/dossiers/{id}/journal user id Bearer token + PermRead List journal entries. /repos/inou-portal/api/api_v1.go:738
GET /api/v1/dossiers/{id}/journal/{entryId} user id, entryId Bearer token + PermRead Get journal entry. /repos/inou-portal/api/api_v1.go:777
POST /api/v1/dossiers/{id}/journal user id Bearer token + PermWrite Create journal entry. /repos/inou-portal/api/api_v1.go:804
PATCH /api/v1/dossiers/{id}/journal/{entryId} user id, entryId Bearer token + PermWrite Update journal entry. /repos/inou-portal/api/api_v1.go:864
POST /api/v1/dossiers/{id}/parse user id Bearer token + PermRead Parse medical data with LLM. /repos/inou-portal/api/api_v1.go:444
GET /api/v1/images/{id} user id Bearer token + PermRead Get image file. /repos/inou-portal/api/api_v1.go:608
GET /api/v1/categories user None Bearer token (optional) List entry categories. /repos/inou-portal/api/api_v1.go:667
GET /api/v1/categories/{name}/types anon name None List types within category. /repos/inou-portal/api/api_v1.go:684

Total Network-Accessible Endpoints: 80+ endpoints across portal, API, OAuth, and MCP servers.

5. Potential Input Vectors for Vulnerability Analysis

Network Surface Focus: Only includes input vectors accessible through the target web application's network interface. Excludes inputs from local-only scripts, build tools, development utilities, or components unreachable via network requests.

URL Parameters

Dossier/Object Identifiers:

  • id (path parameter) - Dossier ID in /dossier/{id}/* routes
  • dossierId (path parameter) - Dossier ID in various routes
  • fileId (path parameter) - File ID in /dossier/{id}/files/{fileId}/*
  • trackerId (path parameter) - Tracker ID in tracker routes
  • entryId (path parameter) - Entry ID in V1 API routes
  • granteeId (path parameter) - User ID in access management routes
  • dossier (query parameter) - Filter parameter in API endpoints

Filtering & Display:

  • token - Authentication token (query parameter fallback)
  • lang - Language code (defaults to "en")
  • wc - Window center for image rendering (float)
  • ww - Window width for image rendering (float)
  • maxdim - Max dimension for image scaling (integer)
  • full - Full image flag (boolean "1")
  • add - Tracker type to add (e.g., ?add=vital)

OAuth Parameters:

  • client_id - OAuth client identifier
  • redirect_uri - OAuth redirect URL (whitelist validated)
  • response_type - OAuth response type (must be "code")
  • state - OAuth state parameter (passed through)
  • code_challenge - PKCE code challenge
  • code_challenge_method - PKCE method (must be "S256")
  • code - Authorization code
  • code_verifier - PKCE code verifier

File Locations: /repos/inou-portal/api/api_v1.go:27, /repos/inou-portal/api/api_image.go:131-141, /repos/inou-portal/portal/oauth.go:59-64

POST Body Fields (Form-Encoded)

Authentication:

  • email - Email address (normalized: toLowerCase + trimSpace) - /repos/inou-portal/portal/main.go:543
  • code - 6-digit verification code (converted to int) - /repos/inou-portal/portal/main.go:563,566
  • nonce - Bot detection timing value (must be >= 2000) - /repos/inou-portal/portal/main.go:542

Profile Management:

  • name - User display name (trimSpace) - /repos/inou-portal/portal/main.go:601
  • dob - Date of birth (YYYY-MM-DD format, validated >= 1900-01-01) - /repos/inou-portal/portal/main.go:602,608-611
  • sex - Gender (converted: "" → 0, "M" → 1, "F" → 2) - /repos/inou-portal/portal/main.go:603,622-623
  • email_lang - Email language preference - /repos/inou-portal/portal/main.go:920
  • relation - Relationship type (converted to int) - /repos/inou-portal/portal/main.go:921,937
  • is_care_receiver - Care receiver flag (boolean "1" check) - /repos/inou-portal/portal/main.go:922

Access Sharing:

  • recipient_name - Name of person being granted access (trimSpace) - /repos/inou-portal/portal/main.go:1216
  • can_edit - Edit permission flag (boolean "1" check) - /repos/inou-portal/portal/main.go:1220
  • action - Grant or revoke action - /repos/inou-portal/portal/main.go:1396
  • role - Permission role template (parent/guardian/caregiver/medical/custom) - /repos/inou-portal/portal/main.go:1401
  • op_w, op_d, op_m - Write/delete/manage permission flags (boolean "1") - /repos/inou-portal/portal/main.go:1420-1422

File Upload:

  • file - Multipart file upload (max 10GB, no type validation) - /repos/inou-portal/portal/upload.go:174
  • path - Relative file path (defaults to filename, uses filepath.Base) - /repos/inou-portal/portal/upload.go:182-186
  • category - File category (genetics triggers genome processing) - /repos/inou-portal/portal/upload.go:187

Tracker Responses:

  • tracker_id - Tracker ID being responded to - /repos/inou-portal/portal/trackers.go:368
  • action - Tracker action - /repos/inou-portal/portal/trackers.go:369
  • response_raw - JSON tracker response data - /repos/inou-portal/portal/trackers.go:384

OAuth:

  • grant_type - OAuth grant type (authorization_code/refresh_token) - /repos/inou-portal/portal/oauth.go:169
  • client_secret - OAuth client secret (bcrypt validated) - /repos/inou-portal/portal/oauth.go:173
  • refresh_token - Refresh token for token rotation - /repos/inou-portal/portal/oauth.go:174

POST Body Fields (JSON-Encoded)

Mobile Authentication:

POST /api/v1/auth/send
{
  "email": "string"  // No validation
}

File: /repos/inou-portal/portal/api_mobile.go:66

Entry/Journal Creation:

POST /api/v1/dossiers/{id}/journal
{
  "dossier": "string",       // Dossier ID
  "category": "string",      // Must be valid category (validated)
  "type": "string",          // Entry type (no validation)
  "value": "string",         // Entry value (no validation)
  "summary": "string",       // Entry summary (no validation)
  "data": "string/json",     // JSON data (no validation)
  "timestamp": int64,        // Unix timestamp (no validation)
  "delete": bool,            // Deletion flag
  "delete_category": bool    // Delete all in category flag
}

File: /repos/inou-portal/api/api_entries.go:71-86

Access Grant/Revoke:

POST /api/access (localhost only)
{
  "accessor": "string",  // Hex ID (required)
  "target": "string",    // Hex ID (required)
  "relation": int,       // Relation type
  "can_edit": bool,      // Permission flag
  "delete": bool,        // Revoke flag
  "touch": bool          // Update timestamp flag
}

File: /repos/inou-portal/api/api_access.go:108-142

Dossier Creation:

POST /api/dossier
{
  "email": "string",      // Required, no format validation
  "invited_by": "string"  // Optional inviter ID
}

File: /repos/inou-portal/api/api_dossier.go:82-85

MCP JSON-RPC:

POST /mcp
{
  "method": "string",     // tools/list, tools/call, prompts/get, etc.
  "params": {...}         // Varies by method
}

File: /repos/inou-portal/portal/mcp_http.go:249

HTTP Headers

Authentication:

  • Authorization: Bearer {token} - Primary authentication method for APIs
    • Accepts OAuth access tokens (encrypted JSON) OR session tokens (64-char hex)
    • File: /repos/inou-portal/api/auth.go:28-38

CORS (MCP/Mobile API):

  • Origin - Not validated (wildcard CORS: Access-Control-Allow-Origin: *)
    • File: /repos/inou-portal/portal/mcp_http.go:180, /repos/inou-portal/portal/api_mobile.go:21

Custom Headers:

  • None identified in source code

Authentication:

  • login - Portal session cookie (30-day expiry)
    • Value: Plain dossier ID (16-char hex)
    • Attributes: HttpOnly, Secure, SameSite=Lax
    • File: /repos/inou-portal/portal/main.go:311-313

Session State:

  • session - Alternative session cookie (used by some API endpoints)
    • Value: Session token (64-char hex)
    • File: /repos/inou-portal/api/auth.go:41-47

OAuth Flow State:

  • oauth_return - Stores return URL during OAuth login flow
    • File: /repos/inou-portal/portal/oauth.go:109-115

Preferences:

  • No preference cookies identified (language stored server-side)

File Upload Vectors

Multipart Form Data:

  • Endpoint: POST /dossier/{id}/upload
  • Field: file (multipart file)
  • Max Size: 10GB (10 << 30 bytes)
  • Type Validation: NONE - All file types accepted (.exe, .sh, .dll, etc.)
  • Extension Filtering: NONE
  • MIME Validation: NONE
  • Processing: Encrypted and stored at /tank/inou/uploads/{dossierID}/{fileID}
  • Special Handling:
    • category=genetics → Triggers genome CSV parsing (async goroutine)
    • DICOM files → Processed by external binary /tank/inou/bin/import-dicom
  • File: /repos/inou-portal/portal/upload.go:153-259

Genome File Format (CSV):

  • Comma or tab delimited
  • Columns: rsid, chromosome, position, genotype
  • No schema validation before parsing
  • File: /repos/inou-portal/portal/genome.go:24-31

6. Network & Interaction Map

Network Surface Focus: Only maps components that are part of the deployed, network-accessible infrastructure. Excludes local development environments, build CI systems, local-only tools, or components unreachable through the target application's network interface.

6.1 Entities

Title Type Zone Tech Data Notes
Internet-Users ExternAsset Internet Browser/Mobile N/A External users accessing the application
Portal-Server Service Edge Go/net-http PII, Tokens, Secrets Main web application server (port 8443)
API-Server Service App Go/net-http PII, Medical, Tokens Backend API service (port 8082, localhost)
DICOM-Viewer Service App Orthanc-like Medical-Imaging Medical imaging viewer (port 8765, localhost)
SQLite-Main DataStore Data SQLite PII, Medical, Tokens Main database (inou.db) with AES-256-GCM encryption
SQLite-Auth DataStore Data SQLite OAuth-Tokens OAuth database (auth.db) - PLAINTEXT refresh tokens
Filesystem-Uploads DataStore Data Linux-FS Medical-Files Encrypted file storage (/tank/inou/uploads/)
Master-Key-File DataStore Data Linux-FS Secrets AES master key (/tank/inou/master.key)
DICOM-Processor Service App External-Binary Medical-Imaging DICOM import tool (/tank/inou/bin/import-dicom)
SMTP-Server ThirdParty ThirdParty Email Public Email delivery for verification codes
Gemini-API ThirdParty ThirdParty Google-AI Medical LLM for parsing medical data
Claude-Desktop ExternAsset Internet Anthropic-MCP N/A AI assistant connecting via OAuth/MCP
ChatGPT ExternAsset Internet OpenAI-MCP N/A AI assistant (potential integration)

6.2 Entity Metadata

Title Metadata
Portal-Server Hosts: https://inou.com:8443; Endpoints: /, /dossier/*, /oauth/*, /mcp, /api/v1/auth/*; Auth: Cookie, Bearer; Dependencies: API-Server, SQLite-Main, SQLite-Auth, Filesystem-Uploads, DICOM-Viewer
API-Server Hosts: http://127.0.0.1:8082; Endpoints: /api/*, /image/*; Auth: Bearer, Session, Localhost-Bypass; Dependencies: SQLite-Main, Filesystem-Uploads; Exposure: Localhost-Only
DICOM-Viewer Hosts: http://127.0.0.1:8765; Endpoints: /viewer/*, /data/*; Auth: Proxied; Dependencies: Filesystem-Uploads; Exposure: Localhost-Only
SQLite-Main Path: /tank/inou/inou.db; Tables: entries, access, dossiers; Encryption: AES-256-GCM; Consumers: Portal-Server, API-Server; Schema: Custom ORM with struct tags
SQLite-Auth Path: /tank/inou/auth.db; Tables: oauth_clients, oauth_codes, oauth_refresh_tokens; Encryption: NONE (plaintext tokens); Consumers: Portal-Server; Risk: High
Filesystem-Uploads Path: /tank/inou/uploads/{dossierID}/{fileID}; Encryption: AES-256-GCM; Types: DICOM, PDF, Images, Genetics-CSV; Max-Size: 10GB; Validation: None
Master-Key-File Path: /tank/inou/master.key; Format: 32-byte AES key; Access: Portal-Server, API-Server on startup; Protection: Filesystem permissions
DICOM-Processor Path: /tank/inou/bin/import-dicom; Args: dossierID, tempDir; Trigger: POST /dossier/{id}/process-imaging; Risk: Command injection potential
SMTP-Server Protocol: SMTP; Usage: Sends 6-digit verification codes; Config: Environment variables; Rate-Limiting: None
Gemini-API Endpoint: https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent; Auth: API key; Usage: Parse medical documents; Data-Sent: Medical-PII
Claude-Desktop OAuth-Client: Anthropic; Redirect-URIs: claude.ai/api/mcp/auth_callback, localhost:6274/oauth/callback; Grant: authorization_code + PKCE; Scope: Read medical data

6.3 Flows (Connections)

FROM → TO Channel Path/Port Guards Touches
Internet-Users → Portal-Server HTTPS :8443 / None Public
Internet-Users → Portal-Server HTTPS :8443 /start None Public
Internet-Users → Portal-Server HTTPS :8443 /send-code None Public
Internet-Users → Portal-Server HTTPS :8443 /verify None Public
Internet-Users → Portal-Server HTTPS :8443 /dashboard auth:user PII
Internet-Users → Portal-Server HTTPS :8443 /dossier/{id} auth:user, ownership:read PII, Medical
Internet-Users → Portal-Server HTTPS :8443 /dossier/{id}/upload auth:user, ownership:write Medical-Files
Internet-Users → Portal-Server HTTPS :8443 /dossier/{id}/permissions auth:user, ownership:manage PII
Internet-Users → Portal-Server HTTPS :8443 /dossier/{id}/share auth:user, ownership:manage PII
Internet-Users → Portal-Server HTTPS :8443 /oauth/authorize auth:user Tokens
Internet-Users → Portal-Server HTTPS :8443 /oauth/token client-auth Tokens
Internet-Users → Portal-Server HTTPS :8443 /mcp auth:bearer PII, Medical
Claude-Desktop → Portal-Server HTTPS :8443 /oauth/authorize auth:user Tokens
Claude-Desktop → Portal-Server HTTPS :8443 /mcp auth:bearer PII, Medical
Portal-Server → API-Server HTTP :8082 /api/* proxy-only PII, Medical, Tokens
Portal-Server → DICOM-Viewer HTTP :8765 /viewer/* proxy-only Medical-Imaging
Portal-Server → SQLite-Main File inou.db None PII, Medical, Tokens, Secrets
Portal-Server → SQLite-Auth File auth.db None OAuth-Tokens
Portal-Server → Filesystem-Uploads File /uploads/* None Medical-Files
Portal-Server → Master-Key-File File master.key (startup) None Secrets
Portal-Server → SMTP-Server SMTP :25/:587 None Public (codes only)
API-Server → SQLite-Main File inou.db None PII, Medical, Tokens
API-Server → Filesystem-Uploads File /uploads/* None Medical-Files
API-Server → Master-Key-File File master.key (startup) None Secrets
API-Server → DICOM-Processor Process exec /bin/import-dicom localhost-only Medical-Imaging
API-Server → Gemini-API HTTPS generativelanguage.googleapis.com api-key Medical-PII
DICOM-Viewer → Filesystem-Uploads File /uploads/* None Medical-Imaging
Localhost → API-Server HTTP :8082 /api/access localhost-bypass PII, Tokens
Localhost → API-Server HTTP :8082 /api/entries localhost-bypass PII, Medical

6.4 Guards Directory

Guard Name Category Statement
auth:user Auth Requires valid user session via cookie login or Bearer token with dossier ID
auth:bearer Auth Requires Authorization: Bearer header with encrypted access token or session token
client-auth Auth Requires OAuth client_id and client_secret (bcrypt validated for confidential clients)
ownership:read ObjectOwnership Requires PermRead (bit 1) on target dossier - checks access table or self-access
ownership:write ObjectOwnership Requires PermWrite (bit 2) on target dossier - allows data modification
ownership:delete ObjectOwnership Requires PermDelete (bit 4) on target dossier - allows entry/file deletion
ownership:manage ObjectOwnership Requires PermManage (bit 8) on target dossier - allows granting access to others
proxy-only Network Only accessible via reverse proxy from Portal-Server, not directly from internet
localhost-only Network Restricted to requests from 127.0.0.1 or ::1 (localhost bypass grants SystemContext)
localhost-bypass Authorization CRITICAL: Localhost requests bypass all RBAC checks and get full database access
api-key Protocol Requires API key for Google Gemini LLM service
cors:wildcard Network WARNING: Access-Control-Allow-Origin: * on MCP and mobile API endpoints

7. Role & Privilege Architecture

This section maps the application's authorization model. The application uses a capability-based RBAC system with bitmask permissions rather than traditional roles.

7.1 Discovered Roles

Important: This application does NOT use traditional roles. Instead, it uses a permission bitmask system with relationship labels.

Role Name Privilege Level Scope/Domain Code Implementation
anon 0 Global No authentication - public endpoints only
user (authenticated) 1 Global Any authenticated user with valid session/token
owner (self-access) 10 Per-Dossier Implicit full permissions when accessorID == dossierID
viewer (PermRead) 3 Per-Dossier Permission bit 1 - can read dossier entries
editor (PermWrite) 5 Per-Dossier Permission bits 1+2 - can read and write
deleter (PermDelete) 6 Per-Dossier Permission bits 1+2+4 - can read, write, delete
manager (PermManage) 8 Per-Dossier Permission bits 1+2+8 - can read, write, grant access
system (SystemContext) 99 Global Full unrestricted access (localhost bypass)

Relationship Labels (stored as Relation field, not enforced):

  • 0: Custom/unspecified
  • 1: Parent
  • 4: Guardian
  • 5: Caregiver
  • 7: Medical professional
  • 99: Demo access

Code Locations:

  • Permission bits: /repos/inou-portal/lib/rbac.go:10-14
  • Relationship labels: /repos/inou-portal/portal/main.go:1432-1435
  • Self-access check: /repos/inou-portal/lib/rbac.go:23-25
  • CheckAccess function: /repos/inou-portal/lib/rbac.go:19-54

7.2 Privilege Lattice

Privilege Ordering (→ means "can access resources of"):

System Context (localhost) → [Full Bypass]
Owner (self-access) → [All Permissions on Own Dossier]
Manager (PermManage = 8) → [Can grant permissions to others]
Deleter (PermDelete = 4) → [Can delete entries]
Editor (PermWrite = 2) → [Can modify data]
Viewer (PermRead = 1) → [Can read data]
User (authenticated) → [Can access own dossier only]
Anon → [Public pages only]

Permission Combination Rules:
- PermManage typically includes PermRead + PermWrite (bitmask 11 = 1+2+8)
- PermDelete includes PermRead + PermWrite (bitmask 7 = 1+2+4)
- Permissions are checked via bitwise AND: (grant.Ops & requestedPerm) != 0

Isolation:
- Dossiers are isolated by default
- Access requires explicit grant in `access` table
- No cross-dossier permission inheritance

No hierarchical role system - each dossier maintains independent access control.

7.3 Role Entry Points

Role Default Landing Page Accessible Route Patterns Authentication Method
anon / /, /start, /pricing, /demo, /privacy-policy, /legal/* None
user /dashboard /dashboard, /onboard, /invite, /connect, /api, /dossier/add Cookie login or Bearer token
viewer /dossier/{id} /dossier/{id} (read-only), /dossier/{id}/audit, /dossier/{id}/export PermRead check
editor /dossier/{id} All viewer routes + /dossier/{id}/edit, /dossier/{id}/upload, /dossier/{id}/trackers/respond PermWrite check
manager /dossier/{id}/permissions All editor routes + /dossier/{id}/permissions, /dossier/{id}/share, /dossier/{id}/revoke, /dossier/{id}/access/* PermManage check
owner /dossier/{id} All routes for owned dossier (implicit full permissions) Self-access (accessorID == dossierID)
system Any All endpoints (RBAC bypassed) Localhost (127.0.0.1 or ::1)

7.4 Role-to-Code Mapping

Role/Permission Middleware/Guards Permission Checks Storage Location
anon None No checks N/A
user getLoggedInDossier() (portal), getAccessContextOrFail() (API) Cookie or token exists Cookie login or Dossier.SessionToken field
viewer (PermRead) requireDossierAccess(), v1CanAccess() CheckAccess(accessor, dossier, dossier, PermRead) access table: Ops & 1 != 0
editor (PermWrite) requireEntryAccess(..., 'w'), v1CanWrite() CheckAccess(accessor, dossier, entry, PermWrite) access table: Ops & 2 != 0
deleter (PermDelete) requireEntryAccess(..., 'd') CheckAccess(accessor, dossier, entry, PermDelete) access table: Ops & 4 != 0
manager (PermManage) requireManageAccess(), CanManageDossier() CheckAccess(accessor, dossier, dossier, PermManage) access table: Ops & 8 != 0
owner (self) None (implicit) accessorID == dossierID No storage (logic-based)
system systemContextForLocalhost(), getAccessContextOrSystem() RemoteAddr starts with 127.0.0.1 or [::1] No storage (IP-based)

Code Locations:

  • Portal auth: /repos/inou-portal/portal/main.go:279-285
  • API auth middleware: /repos/inou-portal/api/auth.go:76-131
  • V1 API auth: /repos/inou-portal/api/api_v1.go:16-86
  • Core RBAC: /repos/inou-portal/lib/rbac.go:19-54
  • Localhost bypass: /repos/inou-portal/api/auth.go:151-156

8. Authorization Vulnerability Candidates

This section identifies specific endpoints and patterns that are prime candidates for authorization testing, organized by vulnerability type.

8.1 Horizontal Privilege Escalation Candidates

Ranked list of endpoints with object identifiers that could allow access to other users' resources.

Priority Endpoint Pattern Object ID Parameter Data Type Sensitivity Notes
HIGH GET /dossier/{id} id (dossierID) Medical + PII Complete health records View other users' complete health dossiers
HIGH GET /dossier/{id}/export id Medical + PII Data export Download other users' complete data as JSON
HIGH GET /api/v1/dossiers/{id} id Medical + PII Dossier metadata API access to dossier details
HIGH GET /api/v1/dossiers/{id}/entries id Medical + PII Medical records List all medical entries in dossier
HIGH GET /api/v1/dossiers/{id}/entries/{entryId} id, entryId Medical + PII Specific record Access individual medical records
HIGH GET /image/{id} id (entryID) Medical-Imaging DICOM images Access medical imaging files (MRI, CT, X-ray)
HIGH GET /contact-sheet.webp/{id} id (studyID) Medical-Imaging Image preview Contact sheet for imaging studies
HIGH GET /api/v1/images/{id} id Medical-Imaging Image file API access to medical images
MEDIUM GET /dossier/{id}/audit id Audit logs Access history See who has accessed a dossier
MEDIUM GET /api/v1/dossiers/{id}/access id Access grants Permission list View access grants for dossier
MEDIUM GET /dossier/{id}/trackers id Daily trackers Vitals, symptoms Access daily health check-ins
MEDIUM GET /api/v1/dossiers/{id}/trackers id Tracker data Health metrics API access to tracker responses
MEDIUM GET /api/v1/dossiers/{id}/journal id Journal entries Health notes Access journal/diary entries
MEDIUM GET /api/v1/dossiers/{id}/journal/{entryId} id, entryId Journal entry Personal notes Specific journal entry
MEDIUM GET /api/studies dossier (query) Imaging studies Medical metadata List imaging studies for dossier
MEDIUM GET /api/series dossier (query) Imaging series Medical metadata List imaging series
MEDIUM GET /api/slices dossier, series (query) Image slices Medical metadata List individual image slices
MEDIUM GET /api/labs/tests dossier (query) Lab tests Medical data List lab test types
MEDIUM GET /api/labs/results dossier (query) Lab results Medical data Get lab test results
LOW GET /dossier/{id}/files/{fileId}/status id, fileId Processing status Metadata File processing status

Testing Strategy:

  1. Create two user accounts (User A, User B)
  2. User A creates a dossier with medical data
  3. User B attempts to access User A's dossier using User A's dossier ID
  4. Check if authorization is properly validated

Key IDOR Vectors:

  • Dossier IDs are 16-character hex strings (e.g., a1b2c3d4e5f67890)
  • Entry IDs follow same format
  • IDs are predictable and enumerable
  • No rate limiting on ID enumeration

8.2 Vertical Privilege Escalation Candidates

List endpoints requiring higher privileges, organized by target permission level.

Escalation to PermWrite (from PermRead)

Endpoint Pattern Functionality Risk Level
POST /dossier/{id}/edit Modify dossier profile (name, DOB, sex) HIGH
POST /dossier/{id}/upload Upload medical files HIGH
POST /dossier/{id}/trackers/respond Submit tracker responses MEDIUM
POST /dossier/{id}/files/{fileId}/update Update file metadata MEDIUM
POST /api/v1/dossiers/{id}/journal Create journal entries MEDIUM
PATCH /api/v1/dossiers/{id}/journal/{entryId} Update journal entries MEDIUM
POST /api/trackers/freeform Submit freeform tracker data MEDIUM
POST /api/entries Create/update medical entries HIGH

Escalation to PermDelete (from PermWrite)

Endpoint Pattern Functionality Risk Level
DELETE /dossier/{id}/files/{fileId}/delete Delete uploaded files HIGH
POST /api/entries with delete: true Delete medical entries HIGH
POST /api/entries with delete_category: true Delete entire category of entries CRITICAL

Escalation to PermManage (from PermDelete)

Endpoint Pattern Functionality Risk Level
POST /dossier/{id}/permissions Grant access to other users CRITICAL
POST /dossier/{id}/share Send access invitations CRITICAL
POST /dossier/{id}/revoke Revoke access from users HIGH
POST /dossier/{id}/rbac/{role} Edit role-based permissions CRITICAL
POST /dossier/{id}/access/{granteeId} Edit specific access grants CRITICAL

Escalation to Owner (from PermManage)

Attack Vector Functionality Risk Level
Modify own access grant to include all permissions Self-escalation via permission manipulation CRITICAL
Grant PermManage to colluding user, have them grant back higher perms Circular permission escalation HIGH

8.3 Context-Based Authorization Candidates

Multi-step workflow endpoints that assume prior steps were completed or check state incorrectly.

Workflow Endpoint Expected Prior State Bypass Potential Risk
Onboarding POST /onboard Email verification completed Direct POST with cookie of unverified dossier MEDIUM
File Processing POST /dossier/{id}/process-imaging Files uploaded via /upload Direct call without upload, manipulate file paths HIGH
OAuth Authorization POST /oauth/token Authorization code issued Code replay, code injection, PKCE bypass MEDIUM
Access Grant POST /dossier/{id}/permissions User has PermManage Check if permission check happens before or after grant HIGH
Email Verification POST /verify Code sent via /send-code Backdoor code 250365 always works (CRITICAL) CRITICAL
File Upload → Processing 1) Upload, 2) Process Files in database Path traversal in relPath parameter between steps CRITICAL

Workflow State Bypass Opportunities:

  1. Email Verification Backdoor: Code 250365 always validates (testing backdoor left in production)

    • Location: /repos/inou-portal/lib/dbcore.go:347, /repos/inou-portal/portal/api_mobile.go:128
    • Impact: Complete authentication bypass
  2. File Processing Path Traversal: Upload stores user-controlled relPath, later used unsafely in file operations

    • Location: /repos/inou-portal/portal/upload.go:222 (storage), Line 455 (unsafe use)
    • Impact: Arbitrary file write on server filesystem
  3. OAuth State Validation: Check if state parameter is properly validated to prevent CSRF

    • Location: /repos/inou-portal/portal/oauth.go:62
    • Impact: OAuth CSRF attacks
  4. Permission Modification While Accessing: Check if concurrent permission changes affect ongoing requests

    • Impact: Time-of-check-time-of-use (TOCTOU) race conditions

8.4 Additional Authorization Testing Targets

SystemContext Bypass Vulnerabilities:

Attack Vector Location Impact
Localhost IP spoofing (X-Forwarded-For, X-Real-IP headers) /repos/inou-portal/api/auth.go:151-156 Full system access if headers trusted
SystemAccessorID reuse (7b3a3ee1c2776dcd) /repos/inou-portal/lib/config.go:27, /repos/inou-portal/lib/rbac.go:20-22 RBAC bypass if ID can be injected
Empty accessorID injection /repos/inou-portal/lib/rbac.go:20-22 RBAC bypass if validation missing

Cross-Dossier Access:

Scenario Endpoints Test
Multi-entry operations POST /api/entries (bulk create) Check if all entries validated for same dossier
Access grant modification POST /dossier/{id}/access/{granteeId} Modify grants for dossiers not owned
Audit log access GET /api/audit Access audit logs for other dossiers

9. Injection Sources

TASK AGENT FINDING: Comprehensive injection source analysis completed. Only network-accessible endpoints included.

9.1 Path Traversal / File Inclusion

CRITICAL: File Upload Path Traversal

Severity: HIGH Endpoint: POST /dossier/{id}/uploadPOST /dossier/{id}/process-imaging Network-Accessible: Yes

Data Flow:

  1. Input Vector: path form parameter in file upload

    • File: /repos/inou-portal/portal/upload.go:182-186
    • Sanitization: filepath.Base() applied, BUT...
  2. Storage: User-controlled path stored in UploadData.RelPath

    • File: /repos/inou-portal/portal/upload.go:222
  3. Dangerous Sink: Path used unsafely in file write operation

    • File: /repos/inou-portal/portal/upload.go:455
    • Code: outPath := filepath.Join(tempDir, relPath)
    • Vulnerability: No validation that result stays within tempDir

Attack Payload:

POST /dossier/1234567890abcdef/upload
Content-Type: multipart/form-data

------WebKitFormBoundary
Content-Disposition: form-data; name="path"

../../../../etc/cron.d/malicious
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="evil.sh"

* * * * * root /tmp/backdoor.sh
------WebKitFormBoundary--

Impact: Arbitrary file write, potential remote code execution

Remediation Location: /repos/inou-portal/portal/upload.go:455 - Add path validation before os.WriteFile()


9.2 Command Injection

MEDIUM: Unvalidated Command Argument

Severity: MEDIUM Endpoint: POST /dossier/{id}/process-imaging Network-Accessible: Yes

Data Flow:

  1. Input Vector: {id} path parameter (extracted from URL)

    • File: /repos/inou-portal/portal/upload.go:388
    • Code: targetID := parts[2]
  2. Dangerous Sink: Passed to external command without validation

    • File: /repos/inou-portal/portal/upload.go:472
    • Code: cmd := exec.Command("/tank/inou/bin/import-dicom", targetID, tempDir)

Mitigation Present: Go's exec.Command() does NOT use shell interpretation, preventing classic command injection (;, |, &&, etc.)

Remaining Risk: If /tank/inou/bin/import-dicom interprets targetID unsafely, exploitation possible

Recommendation: Validate targetID is 16-character hexadecimal: /repos/inou-portal/portal/upload.go:388


9.3 SQL Injection

Status: NOT VULNERABLE Finding: All database queries use parameterized statements

Safe Pattern Example:

// /repos/inou-portal/lib/dbcore.go:166-202
q := "SELECT ... FROM entries WHERE DossierID = ?"
args := []any{dossierID}
if f.Type != "" {
    q += " AND Type = ?"
    args = append(args, Pack([]byte(f.Type)))
}
rows, err := db.Query(q, args...)

Safety Mechanisms:

  • User input passed via ? placeholders
  • Dynamic SQL construction limited to table/column names (not user-controlled)
  • ORM layer encrypts data before parameterization

Code Locations: All queries in /repos/inou-portal/lib/db_queries.go, /repos/inou-portal/lib/dbcore.go


9.4 Server-Side Template Injection (SSTI)

Status: NOT VULNERABLE Finding: Templates pre-loaded from disk, user data passed as structured objects

Safe Pattern:

// /repos/inou-portal/portal/main.go:230
templates = template.Must(template.New("").Funcs(funcs).ParseGlob(filepath.Join(tmplDir, "*.tmpl")))

// /repos/inou-portal/portal/upload.go:150
templates.ExecuteTemplate(w, "base.tmpl", data)

Safety Mechanisms:

  • Templates loaded at startup (not runtime)
  • User data in PageData structs rendered with auto-escaping
  • No dynamic template parsing of user input

9.5 Unsafe Deserialization

Status: NOT VULNERABLE Finding: Go's encoding/json library is safe (no code execution during unmarshal)

Analysis:

// /repos/inou-portal/api/api_entries.go:71-72
var requests []EntryRequest
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&requests); err != nil {
    http.Error(w, "Invalid JSON", http.StatusBadRequest)
    return
}

Why Safe: Unlike Python pickle or Java serialization, Go's JSON unmarshaling only populates struct fields without executing code


9.6 Other Injection Vectors

LDAP/XML/XPath Injection

Status: NOT APPLICABLE - No LDAP, XML, or XPath processing found

NoSQL Injection

Status: NOT APPLICABLE - Uses SQLite with parameterized queries only

Email Header Injection

Status: ⚠️ LOW RISK - Email library likely sanitizes, but not verified

  • Email sending: /repos/inou-portal/portal/main.go:429-436 (sendCodeEmail)
  • User-controlled: Email address and verification code
  • Recommendation: Verify SMTP library sanitizes headers

9.7 Summary of Injection Findings

Type Status Severity Location Network-Accessible
Path Traversal VULNERABLE HIGH /repos/inou-portal/portal/upload.go:455 Yes
Command Injection ⚠️ PARTIAL MEDIUM /repos/inou-portal/portal/upload.go:472 Yes
SQL Injection SAFE N/A All queries use parameterization Yes
SSTI SAFE N/A Templates pre-loaded Yes
Deserialization SAFE N/A Go JSON is safe Yes
Email Header Injection ⚠️ UNKNOWN LOW /repos/inou-portal/portal/main.go:429 Yes

Critical Remediation Needed:

  1. File upload path traversal validation (HIGH priority)
  2. Command argument validation (MEDIUM priority)
  3. Email header sanitization verification (LOW priority)