Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[vulnerability][acl] Acl for writable Object.prototype properties can contaminate access policy objects #398

Closed
t2ym opened this issue Nov 3, 2020 · 0 comments

Comments

@t2ym
Copy link
Owner

t2ym commented Nov 3, 2020

[vulnerability][acl] Acl for writable Object.prototype properties can contaminate access policy objects

Root Cause

  • Acl for writable Object.prototype properties allows the targeted code to alter Object.prototype
  • Access policy objects in ACL which are chained to Object.prototype have inherited properties from Object.prototype

Reproducible Code

Object.prototype.length = 42; // this can contaminate access policy objects for length property

Notes

Fix

  • Add targetConfig.policy.unchainAcl: true configuration option to enable the fix
    • The fix is NOT enabled by default for compatibility
    • Remove the preceding comment of the config to enable the fix
  • Add Policy.unchainAcl(acl) to unlink the chain from policy objects to Object.prototype
diff --git a/demo-config/config.js b/demo-config/config.js
index 175d4365..ade1b885 100644
--- a/demo-config/config.js
+++ b/demo-config/config.js
@@ -824,6 +824,7 @@ class TargetConfig extends Injectable(Traceable(Configurable(GulpDefaultRegistry
       __hook__min: true, // undefined to skip including plugins/policy/__hook__min.js
       // @ifdef argument to include/exclude hookBenchmark() in hook-callback.js
       hookBenchmark: true, // undefined to exclude hookBenchmark()
+      //unchainAcl: true, // @ifdef argument to unchain ACL policy objects if they are chained directly to Object.prototype
       // postfix to the hook callback function name __hook__
       __hook__callback: 'acl', // '': __hook__, 'acl': __hook__acl, 'min': __hook__min
       // TODO: modify plugin/policy/hook-callback.js to use hook.parameters.emptyDocumentUrl
diff --git a/demo-config/policy/basePolicyModule.js b/demo-config/policy/basePolicyModule.js
index 81340a3e..10ce552f 100644
--- a/demo-config/policy/basePolicyModule.js
+++ b/demo-config/policy/basePolicyModule.js
@@ -424,6 +424,21 @@ acl: {
       '@HTMLElement_prototype_reader': 'r--',
       '@Object_prototype_reader': 'r-x',
       '@window_enumerator': 'r--R-',
+/* @ifdef unchainAcl */
+      $toString$: {
+        [S_DEFAULT]: '---',
+        '@Object_assign_reader': 'r--',
+        '@Object_prototype_reader': 'r-x',
+        '@chai_js': 'r--',
+        '@deepcopy': 'r--',
+      },
+      $hasOwnProperty$: {
+        [S_DEFAULT]: '---',
+        '@firebase_auth': 'r--',
+        '@firebase_app': 'r--',
+        '@firebase_database': 'r--',
+      },
+/* @endif */
       [S_INSTANCE]: {
         $__proto__$: 'rwx',
         $constructor$: 'rwxRW',
diff --git a/plugins/policy/Policy.js b/plugins/policy/Policy.js
index 779bda65..b14cad2b 100644
--- a/plugins/policy/Policy.js
+++ b/plugins/policy/Policy.js
@@ -817,6 +817,35 @@ Copyright (c) 2017, 2018, 2019, 2020 Tetsuya Mori <t2y3141592@gmail.com>. All ri
       }
       chainAcl(acl);
     }
+/* @ifdef unchainAcl */
+    static unchainAcl(acl) {
+      const unchainAcl = function unchainAcl(_acl, path = [ [_acl, 'acl'] ]) {
+        let properties = Object.getOwnPropertySymbols(_acl).concat(Object.getOwnPropertyNames(_acl));
+        if (!_acl[S_CHAIN]) {
+          Reflect.setPrototypeOf(_acl, null);
+        }
+        for (let property of properties) {
+          if (property === S_CHAIN) {
+          }
+          else {
+            let __acl = _acl[property];
+            switch (typeof __acl) {
+            case 'object':
+              if (__acl) {
+                path.push([__acl, property]);
+                unchainAcl(__acl, path);
+                path.pop();
+              }
+              break;
+            default:
+              break;
+            }
+          }
+        }
+      }
+      unchainAcl(acl);
+    }
+/* @endif */
     static mergeAcl(target, ...sources) {
       const originalTarget = target;
       const mergeAcl = function mergeAcl(target, source) {
@@ -1352,7 +1381,12 @@ Copyright (c) 2017, 2018, 2019, 2020 Tetsuya Mori <t2y3141592@gmail.com>. All ri
                 case 'undefined':
                   _acl = Reflect.has(_acl, property)
                     ? isGlobal
+/* @ifndef unchainAcl */
                       ? _acl[property] instanceof Object && Reflect.has(_acl[property], S_OBJECT)
+/* @endif */
+/* @ifdef unchainAcl */
+                      ? _acl[property] && typeof _acl[property] === 'object' && Reflect.has(_acl[property], S_OBJECT)
+/* @endif */
                         ? _acl[property][S_OBJECT]
                         : _acl[property]
                       : _acl[property]
@@ -1360,7 +1394,12 @@ Copyright (c) 2017, 2018, 2019, 2020 Tetsuya Mori <t2y3141592@gmail.com>. All ri
                       ? context === S_DEFAULT
                         ? isGlobal
                           ? Reflect.has(acl, property)
+/* @ifndef unchainAcl */
                             ? acl[property] instanceof Object && Reflect.has(acl[property], S_OBJECT)
+/* @endif */
+/* @ifdef unchainAcl */
+                            ? acl[property] && typeof acl[property] === 'object' && Reflect.has(acl[property], S_OBJECT)
+/* @endif */
                               ? acl[property][S_OBJECT]
                               : acl[property]
                             : acl[S_GLOBAL]
@@ -1368,7 +1407,12 @@ Copyright (c) 2017, 2018, 2019, 2020 Tetsuya Mori <t2y3141592@gmail.com>. All ri
                         : _acl[context]
                       : isGlobal
                         ? Reflect.has(acl, property)
+/* @ifndef unchainAcl */
                           ? acl[property] instanceof Object && Reflect.has(acl[property], S_OBJECT)
+/* @endif */
+/* @ifdef unchainAcl */
+                          ? acl[property] && typeof acl[property] === 'object' && Reflect.has(acl[property], S_OBJECT)
+/* @endif */
                             ? acl[property][S_OBJECT]
                             : acl[property]
                           : acl[S_GLOBAL]
@@ -1395,7 +1439,12 @@ Copyright (c) 2017, 2018, 2019, 2020 Tetsuya Mori <t2y3141592@gmail.com>. All ri
                     for (_property of property) {
                       __acl = Reflect.has(_acl, property)
                         ? isGlobal
+/* @ifndef unchainAcl */
                           ? _acl[property] instanceof Object && Reflect.has(_acl[property], S_OBJECT)
+/* @endif */
+/* @ifdef unchainAcl */
+                          ? _acl[property] && typeof _acl[property] === 'object' && Reflect.has(_acl[property], S_OBJECT)
+/* @endif */
                             ? _acl[property][S_OBJECT]
                             : _acl[property]
                           : _acl[property]
@@ -1403,7 +1452,12 @@ Copyright (c) 2017, 2018, 2019, 2020 Tetsuya Mori <t2y3141592@gmail.com>. All ri
                           ? context === S_DEFAULT
                             ? isGlobal
                               ? Reflect.has(acl, property)
+/* @ifndef unchainAcl */
                                 ? acl[property] instanceof Object && Reflect.has(acl[property], S_OBJECT)
+/* @endif */
+/* @ifdef unchainAcl */
+                                ? acl[property] && typeof acl[property] === 'object' && Reflect.has(acl[property], S_OBJECT)
+/* @endif */
                                   ? acl[property][S_OBJECT]
                                   : acl[property]
                                 : acl[S_GLOBAL]
@@ -1411,7 +1465,12 @@ Copyright (c) 2017, 2018, 2019, 2020 Tetsuya Mori <t2y3141592@gmail.com>. All ri
                             : _acl[context]
                           : isGlobal
                             ? Reflect.has(acl, property)
+/* @ifndef unchainAcl */
                               ? acl[property] instanceof Object && Reflect.has(acl[property], S_OBJECT)
+/* @endif */
+/* @ifdef unchainAcl */
+                              ? acl[property] && typeof acl[property] === 'object' && Reflect.has(acl[property], S_OBJECT)
+/* @endif */
                                 ? acl[property][S_OBJECT]
                                 : acl[property]
                               : acl[S_GLOBAL]
diff --git a/plugins/policy/__hook__.js b/plugins/policy/__hook__.js
index c9e8b3cf..2c5dd297 100644
--- a/plugins/policy/__hook__.js
+++ b/plugins/policy/__hook__.js
@@ -328,7 +328,7 @@ Copyright (c) 2017, 2018, 2019, 2020 Tetsuya Mori <t2y3141592@gmail.com>. All ri
             }
           }
         }
-        if (!name && normalizedThisArg instanceof Object) {
+        if (!name && normalizedThisArg instanceof Object/* @ifdef unchainAcl */ || normalizedThisArg === Object.prototype/* @endif */) {
           [name, isStatic, isObject] = detectName(normalizedThisArg, boundParameters);
         }
         let rawProperty = _args[0];
diff --git a/plugins/policy/__hook__acl.js b/plugins/policy/__hook__acl.js
index 288ca14b..f50ac81c 100644
--- a/plugins/policy/__hook__acl.js
+++ b/plugins/policy/__hook__acl.js
@@ -243,7 +243,7 @@ Copyright (c) 2017, 2018, 2019, 2020 Tetsuya Mori <t2y3141592@gmail.com>. All ri
             }
           }
         }
-        if (!name && normalizedThisArg instanceof Object) {
+        if (!name && normalizedThisArg instanceof Object/* @ifdef unchainAcl */ || normalizedThisArg === Object.prototype/* @endif */) {
           [name, isStatic, isObject] = detectName(normalizedThisArg, boundParameters);
         }
         let rawProperty = _args[0];
diff --git a/plugins/policy/hook-callback.js b/plugins/policy/hook-callback.js
index dbb6cb9d..a30a8163 100644
--- a/plugins/policy/hook-callback.js
+++ b/plugins/policy/hook-callback.js
@@ -245,6 +245,9 @@ else {
       'hookBenchmark',
     ]);
     Policy.chainAcl(acl);
+/* @ifdef unchainAcl */
+    Policy.unchainAcl(acl);
+/* @endif */
     Policy.proxyAcl(acl);
     Policy.resolveBareSpecifierAcl(acl);
     Policy.generatePrefixedModuleNames(acl);
@t2ym t2ym closed this as completed in 9a17cb7 Nov 3, 2020
t2ym added a commit that referenced this issue Nov 3, 2020
…rgetConfig.policy.unchainAcl option to avoid policy object contamination via Object.prototype
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant