CVE-2023-2950 (published 2023-05-28T04:15Z)

Improper Authorization in GitHub repository openemr/openemr prior to 7.0.1.

CVSS v3 base score: 8.1, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N)

Weakness information:
cwe_id: CWE-285
cwe_name: Improper Authorization
description: The product does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.
extended_description: 
            Assuming a user with a given identity, authorization is the process of determining whether that user can access a given resource, based on the user's privileges and any permissions or other access-control specifications that apply to the resource.
            When access control checks are not applied consistently - or not at all - users are able to access data or perform actions that they should not be allowed to perform. This can lead to a wide range of problems, including information exposures, denial of service, and arbitrary code execution.
         
         

Fix information:
repo_url: https://github.com/openemr/openemr
commit_details: repo_url: https://github.com/openemr/openemr
author: stephen waite
author_date: 2023-04-12 22:02:09-04:00
msg: fix: bug fix (#6354)

* fix: bug fix

* use attr instead
repository: repo_url: https://github.com/openemr/openemr
repo_name: openemr/openemr
description: The most popular open source electronic health records and medical practice management solution.
repo_language: PHP
owner: openemr
file_changes: filename: OnsiteDocumentController.php
change_type: ModificationType.MODIFY
diff: @@ -251,11 +251,12 @@ class OnsiteDocumentController extends AppBasePortalController
                 $onsitedocument->Pid = $this->SafeGetVal($json, 'pid');
             }
 
-            if (!empty($_SESSION["patient_portal_onsite_two"] ?? null)) {
+            // removing for testing
+            /* if (!empty($_SESSION["patient_portal_onsite_two"] ?? null)) {
                 $decode = $this->SafeGetVal($json, 'fullDocument');
                 $k = (int)$this->SafeGetVal($json, 'csrf_token_form')[0];
                 $json->fullDocument = $this->decode($decode, $k);
-            }
+            } */
 
             $onsitedocument->Facility = $this->SafeGetVal($json, 'facility');
             $onsitedocument->Provider = $this->SafeGetVal($json, 'provider');
@@ -281,6 +282,12 @@ class OnsiteDocumentController extends AppBasePortalController
             if (count($errors) > 0) {
                 $this->RenderErrorJSON('Please check the form for errors', $errors);
             } else {
+                $new_data = $onsitedocument->FullDocument;
+                // use a custom diff function to look for changing tags only with html
+                if ($new_data != strip_tags($new_data)) {
+                    $old_data = $json->fullDocument;
+                    $onsitedocument->FullDocument = $this->htmlDiff($old_data, $new_data);
+                }
                 $onsitedocument->Save();
                 $this->RenderJSON($onsitedocument, $this->JSONPCallback(), true, $this->SimpleObjectParams());
             }
@@ -302,6 +309,7 @@ class OnsiteDocumentController extends AppBasePortalController
             }
             $pk = $this->GetRouter()->GetUrlParam('id');
             $onsitedocument = $this->Phreezer->Get('OnsiteDocument', $pk);
+            $old_data = $onsitedocument->FullDocument;
 
             // only allow patient to update themselves (part 1)
             if (!empty($GLOBALS['bootstrap_pid'])) {
@@ -318,11 +326,12 @@ class OnsiteDocumentController extends AppBasePortalController
                 $onsitedocument->Pid = $this->SafeGetVal($json, 'pid', $onsitedocument->Pid);
             }
 
-            if (!empty($_SESSION["patient_portal_onsite_two"] ?? null)) {
+            // removing for testing
+            /* if (!empty($_SESSION["patient_portal_onsite_two"] ?? null)) {
                 $decode = $this->SafeGetVal($json, 'fullDocument');
                 $k = (int)$this->SafeGetVal($json, 'csrf_token_form')[0];
                 $json->fullDocument = $this->decode($decode, $k);
-            }
+            } */
 
             $onsitedocument->Facility = $this->SafeGetVal($json, 'facility', $onsitedocument->Facility);
             $onsitedocument->Provider = $this->SafeGetVal($json, 'provider', $onsitedocument->Provider);
@@ -348,6 +357,11 @@ class OnsiteDocumentController extends AppBasePortalController
             if (count($errors) > 0) {
                 $this->RenderErrorJSON('Please check the form for errors', $errors);
             } else {
+                // use a custom diff function to look for changing tags only with html
+                $new_data = $onsitedocument->FullDocument;
+                if ($new_data != strip_tags($new_data)) {
+                    $onsitedocument->FullDocument = $this->htmlDiff($old_data, $new_data);
+                }
                 $onsitedocument->Save();
                 $this->RenderJSON($onsitedocument, $this->JSONPCallback(), true, $this->SimpleObjectParams());
             }
@@ -385,11 +399,12 @@ class OnsiteDocumentController extends AppBasePortalController
         }
     }
 
-    /**
+    // removing for testing
+    /*
      * @param $encoded
      * @param $v
      * @return bool|string
-     */
+
     private function decode($encoded, $v): bool|string
     {
         $encoded = base64_decode($encoded);
@@ -401,4 +416,45 @@ class OnsiteDocumentController extends AppBasePortalController
         }
         return base64_decode(base64_decode($decoded));
     }
+    */
+
+    private function diff($old, $new): array
+    {
+        $matrix = array();
+        $maxlen = 0;
+        foreach ($old as $oindex => $ovalue) {
+            $nkeys = array_keys($new, $ovalue);
+            foreach ($nkeys as $nindex) {
+                $matrix[$oindex][$nindex] = isset($matrix[$oindex - 1][$nindex - 1]) ?
+                    $matrix[$oindex - 1][$nindex - 1] + 1 : 1;
+                if ($matrix[$oindex][$nindex] > $maxlen) {
+                    $maxlen = $matrix[$oindex][$nindex];
+                    $omax = $oindex + 1 - $maxlen;
+                    $nmax = $nindex + 1 - $maxlen;
+                }
+            }
+        }
+        if ($maxlen == 0) {
+            return array(array('d' => $old, 'i' => $new));
+        }
+        return array_merge(
+            $this->diff(array_slice($old, 0, $omax), array_slice($new, 0, $nmax)),
+            array_slice($new, $nmax, $maxlen),
+            $this->diff(array_slice($old, $omax + $maxlen), array_slice($new, $nmax + $maxlen))
+        );
+    }
+
+    private function htmlDiff($old, $new): string
+    {
+        $ret = '';
+        $diff = $this->diff(preg_split("/[\s]+/", $old), preg_split("/[\s]+/", $new));
+        foreach ($diff as $k) {
+            if (is_array($k)) {
+                $ret .= (!empty($k['i']) ? attr(implode(' ', $k['i'])) : '');
+            } else {
+                $ret .= $k . ' ';
+            }
+        }
+        return $ret;
+    }
 }

programming_language: PHP
filename: onsitedocuments.js
change_type: ModificationType.MODIFY
diff: @@ -848,9 +848,10 @@ var page = {
             // no frame content is maintained in onsite document activity but template directives are.
             templateContent = templateContent.replace("id=0", "id=" + page.encounterFormId);
         }
-        if (isPortal) {
+        // removing for testing
+        /* if (isPortal) {
             templateContent = page.encode(templateContent, parseInt(csrfTokenDoclib[0]));
-        }
+        } */
         page.onsiteDocument.save({
             'pid': cpid,
             'facility': page.formOrigin, /* 0 portal, 1 dashboard, 2 patient documents */
@@ -956,7 +957,8 @@ var page = {
             }
         });
     },
-    encode: function (content, k) {
+    // removing for testing
+    /* encode: function (content, k) {
         let encoded = "";
         content = btoa(content);
         content = btoa(content);
@@ -966,5 +968,5 @@ var page = {
             encoded = encoded + String.fromCharCode(b);
         }
         return btoa(encoded);
-    }
+    } */
 };

programming_language: JavaScript