Skip to content

Instantly share code, notes, and snippets.

@vvkpd
Created July 3, 2026 11:46
Show Gist options
  • Select an option

  • Save vvkpd/63d9e4d35c7decd680b1e378536a3db4 to your computer and use it in GitHub Desktop.

Select an option

Save vvkpd/63d9e4d35c7decd680b1e378536a3db4 to your computer and use it in GitHub Desktop.
This specification details the implementation plan, configuration footprint, security matrix, and runtime data flows for adding a custom **Hyperlink Control Property** to the Bahmni Forms 2.0 engine

Technical Analysis: Custom Hyperlink Control Property in Forms 2.0

This specification details the implementation plan, configuration footprint, security matrix, and runtime data flows for adding a custom Hyperlink Control Property to the Bahmni Forms 2.0 engine.


1. Executive Summary & Feature Baseline

Feature Definition

  • Custom Form Component: While the baseline Bahmni Forms 2.0 engine natively supports controls like TextBox, NumericBox, ObsControl, Section, Image, and Video, it lacks a native Hyperlink control. This architecture establishes a framework to support hyperlinks configured directly within the form builder infrastructure.
  • Dual-Path Routing: The control supports two distinct operational classifications: Internal paths (which use client-side hash routing parameters unique to the EMR context) and External targets (which navigate away from the application space). the path type is now auto-classified by URL shape (see Section 3).

Core Interoperability Constraints

  • Dashboard Printing Exclusion: Per system-level rules regarding layout transformations, any physical print summary sheet or print action generated from the patient dashboard layout must explicitly omit or strip out hyperlinks entirely to protect document structure. Implemented via a CSS @media print rule that hides all a[data-bahmni-hyperlink] elements.
  • Context Intercept Mechanisms: External links open directly in a new tab; isolation is enforced via HTML security attributes only (see Section 4).

2. Path Convention Specifications

To ensure clear processing and validation rules, the control enforces distinct structural differences between the two route types. Path type is determined automatically at runtime — no explicit toggle is required from the form designer.

A. Internal Hyperlinks

  • Structure: Relative paths only; they must not contain the protocol or hostname.
  • Auto-Detection Rule: Any URL beginning with / is classified as internal.
  • Examples:
    • /bahmni/app/{uuid}
    • /patient/doc.pdf
  • Token Support: {patientUuid} is the only supported token; all other {...} patterns are rejected.
  • Routing Behavior: These paths leverage client-side relative routing within the local EMR deployment domain, maintaining the clinician's active authenticated session state.

B. External Hyperlinks

  • Structure: Complete absolute URLs including host and path information.
  • Auto-Detection Rule: Any URL beginning with https:// is classified as external.
  • Examples:
    • https://abc.com/xyz
    • https://abc.com/doc.pdf
  • Token Support: All {...} tokens are forbidden in external URLs to prevent variable injection leakage.
  • Routing Behavior: These targets open in an entirely separate browser tab using target="_blank".

3. Implementation & Configuration Plan

To preserve the modular boundary between form structure and local server orchestration, validation configurations are isolated from standalone form definitions.

Configuration Placement (app.json)

  • File Directory: The validation array resides inside the module configuration profile deployed on the web server at: /var/www/bahmni_config/openmrs/apps/clinical/app.json
  • Schema Integration: The custom parameter array integrates directly inside the core application configuration block. This mirrors the management architecture used for other system attributes like quickPrints, diagnosisStatus, hideFormName, and obsIgnoreList:
"config": {
  "hyperlinkAllowedDomains": [
    "*.who.int",
    "*.health.gov"
  ]
}

Form Designer User Interface (Implementer Interface)

+-------------------------------------------------------------------------+
| [Top Action Bar: Save / Preview Mode Toggle]                            |
+------------------------------------+------------------------------------+
|                                    | [Right Property Inspector Panel]   |
|                                    |                                    |
| [Left Controls Pane]               | Hyperlink Control Properties       |
| -> Select ObsControl               | - Input URL Field: [             ] |
|                                    | - Label Field:    [             ]  |
|                                    |   (disabled until URL is entered)  |
| [Center Form Details Canvas]       +------------------------------------+
| -> Drag-and-Drop Drop Zone         | [Split Layout Real-Time Feedback]  |
|                                    | Preview on Right (Editable View)   |
|                                    | -> Live Context Component Update   |
+------------------------------------+------------------------------------+

Implementation Note: The system determines path type automatically from the URL value entered. The designer sees two fields only: hyperlinkUrl and hyperlinkLabel.

  • Property Configuration Inspector: Selecting the ObsControl with a hyperlink URL activates two properties in the right-hand panel: hyperlinkUrl (text input, validated on blur) and hyperlinkLabel (text input, disabled until a valid URL is present).
  • Split-Layout Real-Time Feedback: As the designer enters a URL, a live non-navigable anchor preview updates in the properties inspector.
  • Preview Mode Activation: Toggling the top-level Preview tool isolates the workspace canvas, hiding all administrative sidebars to simulate the end-user clinical workspace before changes are committed.

4. Security Architecture & Validation Matrix

Applied Validation Rules

When a hyperlink path is evaluated at runtime by hyperlinkValidator.js, the URL is passed through an inspection pipeline:

Validation Target Mechanism Applied Intended Action
Protocol Scheme Strict String Matching Exclusively passes https:// for external and / for internal; rejects and blocks javascript:, data:, file:, and vbscript:.
Path Type Classification URL Shape Detection Auto-classifies: / prefix → internal, https:// prefix → external, anything else → invalid. No explicit toggle.
Domain Legitimacy Array Iteration Lookup Parses the target domain and evaluates it against the hyperlinkAllowedDomains list from app.json. Supports wildcard patterns (*.who.int). Default-deny: empty list blocks all external URLs.
Context Isolation HTML Property Injection External links: target="_blank", rel="noopener noreferrer", referrerPolicy="no-referrer". Internal links: target="_blank", rel="noopener".
Token Containment Pattern Matching External URLs: all {...} tokens rejected. Internal URLs: only {patientUuid} permitted; all other tokens rejected.
Error Rendering Inline UI Feedback Validation failures render an inline <span class="hyperlink-error"> message; no broken anchor is rendered.

Navigation Confirmation — Not Implemented

The original spec called for a confirmation dialog before external navigation. This was deferred. External links open directly in a new tab. The security boundary is maintained entirely through HTML isolation attributes rather than a user-facing intercept.

System-Level Vulnerability Context

  • Automated Environment Monitoring: The broader ecosystem handles host-level compliance and software library status via server-side scanning layers including OpenVAS, Lynis, OpenSCAP, and Vuls.
  • Vulnerability Bounds: Core platform updates manage foundational system logic vulnerabilities (such as Stored XSS mitigations and OpenMRS authentication state). However, the system contains no native mechanisms to dynamically track the reputation of third-party domains or audit external file contents during live browser sessions.

5. Threat Matrix: Covered vs. Remaining

Status Threat Mitigation
[COVERED] Stored XSS via Script Injection Eliminated by strict protocol checks blocking unsafe URI schemes.
[COVERED] Reverse Tabnabbing Context Hijacking Blocked by injecting rel="noopener noreferrer" on all external links.
[COVERED] Clinical Information Leaks via Referrer referrerPolicy="no-referrer" prevents metadata exposure to external destinations.
[COVERED] Variable/Token Injection in External URLs All {...} tokens rejected in external URLs at validation time.
[DEFERRED] Clinician Navigating Away Without Warning Confirmation popup not implemented; clinician navigates directly to new tab.
[REMAINING] Domain Takeovers & Expiration Compromise An allowed domain could expire or change ownership in the future.
[REMAINING] Drive-By Third-Party Malicious Payloads The application layer cannot intercept malicious binaries or PDFs hosted on destination targets.

6. Path Journey & Flow Architecture

Form properties are captured at design-time via ObsControlDesigner.jsx in /src/components/designer. At runtime, the structural JSON metadata is passed into the root form Container component. The hyperlinkAllowedDomains config is read from app.json and propagated through Container → Section → ObsControl via props. Path type is resolved automatically inside hyperlinkValidator.js.

Journey A: Internal Hyperlink Path

[ Form Builder App ]
        │
        ▼ (Designer enters a relative URL starting with "/"; no type toggle needed)
[ Form Metadata JSON ]
        │
        ▼ (Form loads inside core application Container component)
[ Runtime Rendering Engine ]
        │
        ▼ (hyperlinkValidator.js detects internal path by "/" prefix)
[ Token Resolver ] ──► (Substitutes {patientUuid} if present via Util.resolveUrlTokens())
        │
        ▼ (Generates relative anchor: target="_blank", rel="noopener")
[ Client-Side Router Navigation ]
        │
        ▼ (Standard OpenMRS role permissions & session auth enforced natively)
[ Targeted Internal EMR Application View ]

Journey B: External Hyperlink Path

[ Form Builder App ]
        │
        ▼ (Designer enters an absolute URL starting with "https://"; no type toggle needed)
[ Form Metadata JSON ]
        │
        ▼ (Form loads inside core application Container component)
[ Runtime Rendering Engine ]
        │
        ▼ (hyperlinkValidator.js detects external by "https://" prefix)
        ▼ (Rejects any {token} patterns in URL)
[ Domain Allowlist Check ] ◄─────► Checks target domain against hyperlinkAllowedDomains array
        │
        ├───[ Domain Not Listed ]──► Render inline error message; no anchor rendered
        │
        └───[ Domain Approved ]
                │
                ▼ (Appends: target="_blank", rel="noopener noreferrer", referrerPolicy="no-referrer")
      [ Anchor Rendered — No Confirmation Dialog ]
                │
                ▼ (Clinician clicks link; opens directly in new tab)
      [ External Destination Website / Document ]

7. Impact Analysis of User Misconfiguration

If a form builder bypasses or misconfigures a link validation by mistake, the application handles the failure modes through the following structural behaviors.

Scenario 1: External URL Entered (auto-classified as External)

  • Validator behavior: Detects https:// prefix, runs domain allowlist check. If domain not in hyperlinkAllowedDomains, renders inline error and no anchor.
  • User Experience Result: Designer or clinician sees a hyperlink-error message in place of the link.

Scenario 2: Relative Path Entered (auto-classified as Internal)

  • Validator behavior: Detects / prefix, skips domain check, allows only {patientUuid} token substitution.
  • User Experience Result: Link renders and navigates within the EMR context. No authentication tokens are leaked externally.

Scenario 3: Invalid / Ambiguous URL

  • Validator behavior: Neither / nor https:// prefix — classified as invalid. Inline error rendered.
  • User Experience Result: No broken anchor; error message guides the designer to correct the input.

8. End-to-End System Flow

The diagram below illustrates the complete lifecycle — from form builder configuration through runtime rendering and final navigation — for both internal and external hyperlink paths.

[ IMPLEMENTER INTERFACE (Form Builder) ]
│
├─► Add hyperlinkUrl property to ObsControl
├─► Optionally set hyperlinkLabel for display text
└─► System auto-classifies URL type on save (no toggle)
│
▼
[ FORM METADATA (JSON Schema Registry) ]
│
▼ (Loads into Clinical Workspace during Consultation)
[ BAHMNI APP FORM RENDERING ENGINE ]
│   (hyperlinkAllowedDomains propagated from app.json → Container → ObsControl)
│
├─────────── hyperlinkValidator.js: Detect URL Shape ───────────┤
│                                                               │
▼ [ INTERNAL: starts with "/" ]                                 ▼ [ EXTERNAL: starts with "https://" ]
│                                                               │
Substitute {patientUuid} token                              Reject all {token} patterns
(via Util.resolveUrlTokens)                                     │
│                                                       Read hyperlinkAllowedDomains
▼                                                               │
[ Render Anchor ]                                       Is Domain Approved?
target="_blank"                                        /                   \
rel="noopener"                              [ MATCH ]               [ NO MATCH ]
│                                               /                         \
│                               Render Anchor:                    Render inline error
│                               target="_blank"                   (no anchor rendered)
│                               rel="noopener noreferrer"
│                               referrerPolicy="no-referrer"
│                                               │
▼                               Clinician Clicks ──► Opens new tab directly
Navigate Within EMR App Context                     (no confirmation dialog)

9. Repositories & Key Files

Repository File Purpose
form-controls src/helpers/hyperlinkValidator.js Core validation: protocol check, type detection, domain allowlist, token rules
form-controls src/components/ObsControl.jsx Runtime rendering of hyperlink anchor with security attributes
form-controls src/components/designer/ObsControlDesigner.jsx Designer preview; exposes hyperlinkUrl and hyperlinkLabel properties
form-controls styles/bahmniAppsFormBuilder/_form.scss Styling + @media print exclusion rule
form-controls test/components/ObsControlHyperlink.spec.js 15 component-level tests
form-controls test/helpers/hyperlinkValidator.spec.js 23 validator unit tests
implementer-interface src/form-builder/constants.js Property name constants
implementer-interface src/form-builder/components/Property.jsx onBlur validation trigger for hyperlinkUrl
implementer-interface src/form-builder/components/PropertyEditor.jsx Disables hyperlinkLabel when URL is empty
openmrs-module-bahmniapps ui/app/common/concept-set/directives/formControls.js Reads hyperlinkAllowedDomains from appService config
cure-bahmni-emr openmrs/apps/clinical/app.json Defines hyperlinkAllowedDomains array
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment