• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / 21687891684

04 Feb 2026 08:49PM UTC coverage: 96.222% (+0.001%) from 96.221%
21687891684

push

github

web-flow
Bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 (#14410)

Bumps @isaacs/brace-expansion from 5.0.0 to 5.0.1.

---
updated-dependencies:
- dependency-name: "@isaacs/brace-expansion"
  dependency-version: 5.0.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

41734 of 51310 branches covered (81.34%)

209287 of 217505 relevant lines covered (96.22%)

71.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.2
/libraries/objectGuard/objectGuard.js
1
import {isData, sessionedApplies} from '../../src/activities/redactor.js';
2
import {deepEqual, logWarn} from '../../src/utils.js';
3

4
/**
5
 * @typedef {import('../src/activities/redactor.js').TransformationRuleDef} TransformationRuleDef
6
 * @typedef {import('../src/adapters/bidderFactory.js').TransformationRule} TransformationRule
7
 * @typedef {Object} ObjectGuard
8
 * @property {*} obj a view on the guarded object
9
 * @property {function(): void} verify a function that checks for and rolls back disallowed changes to the guarded object
10
 */
11

12
/**
13
 * Create a factory function for object guards using the given rules.
14
 *
15
 * An object guard is a view on the guarded object that applies "redact" rules (the same rules used in activites/redactor.js),
16
 * and prevents writes (including deltes) that violate "write protect" rules.
17
 *
18
 * This is meant to provide sandboxed version of a privacy-sensitive object, where reads
19
 * are filtered through redaction rules and writes are checked against write protect rules.
20
 *
21
 */
22
export function objectGuard(rules) {
23
  const root = {};
96✔
24

25
  // rules are associated with specific portions of the object, e.g. "user.eids"
26
  // build a tree representation of them, where the root is the object itself,
27
  // and each node's children are properties of the corresponding (nested) object.
28

29
  function invalid() {
30
    return new Error('incompatible redaction rules');
4✔
31
  }
32

33
  rules.forEach(rule => {
96✔
34
    rule.paths.forEach(path => {
495✔
35
      let node = root;
1,920✔
36
      path.split('.').forEach(el => {
1,920✔
37
        node.children = node.children ?? {};
4,442✔
38
        node.children[el] = node.children[el] ?? { parent: node, path: node.path ? `${node.path}.${el}` : el };
4,442✔
39
        node = node.children[el];
4,442✔
40
        node.wpRules = node.wpRules ?? [];
4,442✔
41
        node.redactRules = node.redactRules ?? [];
4,442✔
42
      });
43
      const tag = rule.wp ? 'hasWP' : 'hasRedact';
1,920✔
44
      const ruleset = rule.wp ? 'wpRules' : 'redactRules';
1,920✔
45
      // sanity check: do not allow rules of the same type on related paths,
46
      // e.g. redact both 'user' and 'user.eids'; we don't need and this logic
47
      // does not handle it
48
      if (node[tag] && !node[ruleset]?.length) {
1,920✔
49
        throw invalid();
2✔
50
      }
51
      node[ruleset].push(rule);
1,918✔
52
      let parent = node;
1,918✔
53
      while (parent) {
1,918✔
54
        parent[tag] = true;
6,356✔
55
        if (parent !== node && parent[ruleset]?.length) {
6,356✔
56
          throw invalid();
2✔
57
        }
58
        parent = parent.parent;
6,354✔
59
      }
60
    });
61
  });
62

63
  function getRedactRule(node) {
64
    if (node.redactRule == null) {
262✔
65
      node.redactRule = node.redactRules.length === 0 ? false : {
149✔
66
        check: (applies) => node.redactRules.some(applies),
109✔
67
        get(val) {
68
          for (const rule of node.redactRules) {
12✔
69
            val = rule.get(val);
13✔
70
            if (!isData(val)) break;
13✔
71
          }
72
          return val;
12✔
73
        }
74
      }
75
    }
76
    return node.redactRule;
262✔
77
  }
78

79
  function getWPRule(node) {
80
    if (node.wpRule == null) {
144✔
81
      node.wpRule = node.wpRules.length === 0 ? false : {
82✔
82
        check: (applies) => node.wpRules.some(applies),
131✔
83
      }
84
    }
85
    return node.wpRule;
144✔
86
  }
87

88
  /**
89
   * clean up `newValue` so that it doesn't violate any write protect rules
90
   * when set onto the property represented by 'node'.
91
   *
92
   * This is done substituting (portions of) `curValue` when some rule is violated.
93
   */
94
  function cleanup(node, curValue, newValue, applies) {
95
    if (
114✔
96
      !node.hasWP ||
345✔
97
      (!isData(curValue) && !isData(newValue)) ||
98
      deepEqual(curValue, newValue)
99
    ) {
100
      return newValue;
88✔
101
    }
102
    const rule = getWPRule(node);
26✔
103
    if (rule && rule.check(applies)) {
26✔
104
      return curValue;
14✔
105
    }
106
    if (node.children) {
12✔
107
      for (const [prop, child] of Object.entries(node.children)) {
12✔
108
        const propValue = cleanup(child, curValue?.[prop], newValue?.[prop], applies);
40✔
109
        if (newValue != null && typeof newValue === 'object') {
40✔
110
          if (!isData(propValue) && !curValue?.hasOwnProperty(prop)) {
39✔
111
            delete newValue[prop];
34✔
112
          } else {
113
            newValue[prop] = propValue;
5✔
114
          }
115
        } else {
116
          logWarn(`Invalid value set for '${node.path}', expected an object`, newValue);
1✔
117
          return curValue;
1✔
118
        }
119
      }
120
    }
121
    return newValue;
11✔
122
  }
123

124
  function isDeleteAllowed(node, curValue, applies) {
125
    if (!node.hasWP || !isData(curValue)) {
5✔
126
      return true;
1✔
127
    }
128
    const rule = getWPRule(node);
4✔
129
    if (rule && rule.check(applies)) {
4✔
130
      return false;
3✔
131
    }
132
    if (node.children) {
1✔
133
      for (const [prop, child] of Object.entries(node.children)) {
1✔
134
        if (!isDeleteAllowed(child, curValue?.[prop], applies)) {
2✔
135
          return false;
1✔
136
        }
137
      }
138
    }
139
    return true;
×
140
  }
141

142
  const TARGET = Symbol('TARGET');
92✔
143

144
  function mkGuard(obj, tree, final, applies, cache = new WeakMap()) {
402✔
145
    // If this object is already proxied, return the cached proxy
146
    if (cache.has(obj)) {
402✔
147
      return cache.get(obj);
113✔
148
    }
149

150
    /**
151
     * Dereference (possibly nested) proxies to their underlying objects.
152
     *
153
     * This is to accommodate usage patterns like:
154
     *
155
     * guardedObject.property = [...guardedObject.property, additionalData];
156
     *
157
     * where the `set` proxy trap would get an already proxied object as argument.
158
     */
159
    function deref(obj, visited = new Set()) {
214✔
160
      if (cache.has(obj?.[TARGET])) return obj[TARGET];
214✔
161
      if (obj == null || typeof obj !== 'object') return obj;
147✔
162
      if (visited.has(obj)) return obj;
33✔
163
      visited.add(obj);
32✔
164
      Object.keys(obj).forEach(k => {
32✔
165
        const sub = deref(obj[k], visited);
81✔
166
        if (sub !== obj[k]) {
81!
167
          obj[k] = sub;
×
168
        }
169
      })
170
      return obj;
32✔
171
    }
172

173
    const proxy = new Proxy(obj, {
289✔
174
      get(target, prop, receiver) {
175
        if (prop === TARGET) return target;
717✔
176
        const val = Reflect.get(target, prop, receiver);
583✔
177
        if (final && val != null && typeof val === 'object') {
583✔
178
          // a parent property has write protect rules, keep guarding
179
          return mkGuard(val, tree, final, applies, cache)
52✔
180
        } else if (tree.children?.hasOwnProperty(prop)) {
531✔
181
          const {children, hasWP} = tree.children[prop];
269✔
182
          if (isData(val)) {
269✔
183
            // if this property has redact rules, apply them
184
            const rule = getRedactRule(tree.children[prop]);
262✔
185
            if (rule && rule.check(applies)) {
262✔
186
              return rule.get(val);
12✔
187
            }
188
          }
189
          if ((children || hasWP) && val != null && typeof val === 'object') {
257✔
190
            // some nested properties have rules, return a guard for the branch
191
            return mkGuard(val, tree.children?.[prop] || tree, final || children == null, applies, cache);
249!
192
          }
193
        }
194
        return val;
270✔
195
      },
196
      set(target, prop, newValue, receiver) {
197
        if (final) {
189✔
198
          // a parent property has rules, apply them
199
          const rule = getWPRule(tree);
109✔
200
          if (rule && rule.check(applies)) {
109✔
201
            return true;
56✔
202
          }
203
        }
204
        newValue = deref(newValue);
133✔
205
        if (tree.children?.hasOwnProperty(prop)) {
133✔
206
          // apply all (possibly nested) write protect rules
207
          const curValue = Reflect.get(target, prop, receiver);
74✔
208
          newValue = cleanup(tree.children[prop], curValue, newValue, applies);
74✔
209
          if (typeof newValue === 'undefined' && !target.hasOwnProperty(prop)) {
74✔
210
            return true;
5✔
211
          }
212
        }
213
        return Reflect.set(target, prop, newValue, receiver);
128✔
214
      },
215
      deleteProperty(target, prop) {
216
        if (final) {
8✔
217
          // a parent property has rules, apply them
218
          const rule = getWPRule(tree);
5✔
219
          if (rule && rule.check(applies)) {
5✔
220
            return true;
4✔
221
          }
222
        }
223
        if (tree.children?.hasOwnProperty(prop) && !isDeleteAllowed(tree.children[prop], target[prop], applies)) {
4✔
224
          // some nested properties should not be deleted
225
          return true;
3✔
226
        }
227
        return Reflect.deleteProperty(target, prop);
1✔
228
      }
229
    });
230

231
    // Cache the proxy before returning
232
    cache.set(obj, proxy);
289✔
233
    return proxy;
289✔
234
  }
235

236
  return function guard(obj, ...args) {
207✔
237
    const session = {};
101✔
238
    return mkGuard(obj, root, false, sessionedApplies(session, ...args))
101✔
239
  };
240
}
241

242
/**
243
 * @param {TransformationRuleDef} ruleDef
244
 * @return {TransformationRule}
245
 */
246
export function writeProtectRule(ruleDef) {
247
  return Object.assign({
140✔
248
    wp: true,
249
  }, ruleDef)
250
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc