Skip to content

Conversation

@cxcorp
Copy link
Contributor

@cxcorp cxcorp commented Mar 1, 2025

From #1025

Fixes an unhandled error occurring when validated objects (e.g. sources) had keys that were found in Object.prototype. This caused certain truthyness checks to incorrectly pass, as e.g. object["__proto__"] always returns a truthy value (unless object has a null prototype or otherwise non-Object prototype).

This caused unhandled errors such as:

TypeError: validateElement is not a function
    at validateObject (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate_object.ts:38:32)
    at Object.validateSource [as source] (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate_source.ts:32:22)
    at validate (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate.ts:96:42)
    at validateObject (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate_object.ts:38:32)
    at validate (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate.ts:99:23)
    at validateObject (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate_object.ts:38:32)
    at validate (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate.ts:99:23)
    at validateStyleMin (node_modules/@maplibre/maplibre-gl-style-spec/src/validate_style.min.ts:35:28)
    at src/fuzz.test.ts:175:11
TypeError: Cannot convert undefined or null to object
    at valueOf (<anonymous>)
    at validateObject (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate_object.ts:38:32)
    at Object.validateSource [as source] (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate_source.ts:32:22)
    at validate (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate.ts:96:42)
    at validateObject (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate_object.ts:38:32)
    at validate (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate.ts:99:23)
    at validateObject (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate_object.ts:38:32)
    at validate (node_modules/@maplibre/maplibre-gl-style-spec/src/validate/validate.ts:99:23)
    at validateStyleMin (node_modules/@maplibre/maplibre-gl-style-spec/src/validate_style.min.ts:35:28)
    at src/fuzz.test.ts:175:11

for sources like:

{
  "version": 8,
  "sources": {
    "openmaptiles": {
      "type": "vector",
      "url": "https://tiles.openfreemap.org/planet",
      "__proto__.": 6
    }
  },
  "layers": []
}
{
  "version": 8,
  "sources": {
    "openmaptiles": {
      "type": "vector",
      "url": "https://tiles.openfreemap.org/planet",
      "valueOf": 6
    }
  },
  "layers": []
}

respectively. Since the issue was in validate_object, which is widely used in the codebase, there are most likely many other inputs that would cause this error, e.g. via layer objects or any other object in the style.

This PR fixes the problem by checking that the value is indeed set on the object via Object.hasOwn(), or
Object.prototype.hasOwnProperty.call() if hasOwn is not available. I was unsure of the browser targets, so since Object.hasOwn was added in ES2022, I thought it would be better to make a separate function for more ergonomic use which falls back to a more compatible implementation.

The unit test for validate_object.test.ts does not test for __proto__ as a key, as jsonlint currently mutates the resulting objects' prototypes due to an assignment instead of Object.defineProperty(), and the resulting objects have the wrong type when checked via instanceof (and thus in getType()). I will open a separate issue about that.

Launch Checklist

  • Confirm your changes do not include backports from Mapbox projects (unless with compliant license) - if you are not sure about this, please ask!
  • Briefly describe the changes in this PR.
  • Link to related issues.
  • Include before/after visuals or gifs if this PR includes visual changes.
  • Write tests for all new functionality.
  • Document any changes to public APIs.
  • Post benchmark scores.
  • Add an entry to CHANGELOG.md under the ## main section.

Fixes an unhandled error occurring when validated objects (e.g. sources)
had keys that were found in Object.prototype. This caused certain
truthyness checks to incorrectly pass, as e.g. object["__proto__"]
always returns a truthy value (unless object has a null prototype or
otherwise non-Object prototype).

This commit fixes the problem by checking that the value is indeed set
on the object via Object.hasOwn(), or
Object.prototype.hasOwnProperty.call() if hasOwn is not available.
@codecov-commenter
Copy link

codecov-commenter commented Mar 1, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 92.74%. Comparing base (d275892) to head (d8fdb0d).
Report is 5 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1028   +/-   ##
=======================================
  Coverage   92.73%   92.74%           
=======================================
  Files         107      108    +1     
  Lines        4722     4728    +6     
  Branches     1344     1347    +3     
=======================================
+ Hits         4379     4385    +6     
  Misses        343      343           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@cxcorp cxcorp marked this pull request as ready for review March 1, 2025 18:42
@HarelM
Copy link
Collaborator

HarelM commented Mar 1, 2025

Can you check why the coverage is complaining?

@cxcorp
Copy link
Contributor Author

cxcorp commented Mar 1, 2025

Can you check why the coverage is complaining?

This was because the environment in which the tests ran do support Object.hasOwn and thus the hasOwn fallback function was never used. I added some unit tests to ensure the fallback mechanism and its function work as intended.

hasOwn can now be used to replace patterns like:

```
if (hasOwnProperty(obj, key) && obj[key]) {}
```

with

```
if (hasOwn(obj, key)) {}
```

whereas getOwn can be used to replace patterns like

```
const value = (hasOwnProperty(obj, key) && obj[key]) || fallback
```

with

```
const value = getOwn(obj, key) || fallback
```
@cxcorp cxcorp force-pushed the fix-validate-object-crashing-for-prototype-keys branch from 8694268 to 2dc7b02 Compare March 2, 2025 22:30
@HarelM
Copy link
Collaborator

HarelM commented Mar 4, 2025

THANKS!

@HarelM HarelM merged commit 91175b3 into maplibre:main Mar 4, 2025
6 checks passed
@cxcorp cxcorp deleted the fix-validate-object-crashing-for-prototype-keys branch March 8, 2025 22:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants