diff --git a/quickjs.c b/quickjs.c index 5287785..9cbb0ac 100644 --- a/quickjs.c +++ b/quickjs.c @@ -13387,8 +13387,14 @@ static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) continue; } - /* check if the property was deleted */ - ret = JS_HasProperty(ctx, it->obj, prop); + // check if the property was deleted unless we're dealing with a proxy + JSValue obj = it->obj; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_PROXY) + break; + } + ret = JS_HasProperty(ctx, obj, prop); if (ret < 0) return ret; if (ret) diff --git a/tests/test_builtin.js b/tests/test_builtin.js index c8ba98e..33561a4 100644 --- a/tests/test_builtin.js +++ b/tests/test_builtin.js @@ -829,6 +829,22 @@ function test_generator() assert(v.value === 6 && v.done === true); } +function test_proxy_iter() +{ + const p = new Proxy({}, { + getOwnPropertyDescriptor() { + return {configurable: true, enumerable: true, value: 42}; + }, + ownKeys() { + return ["x", "y"]; + }, + }); + const a = []; + for (const x in p) a.push(x); + assert(a[0], "x"); + assert(a[1], "y"); +} + /* CVE-2023-31922 */ function test_proxy_is_array() { @@ -865,6 +881,7 @@ test_map(); test_weak_map(); test_weak_set(); test_generator(); +test_proxy_iter(); test_proxy_is_array(); test_exception_source_pos(); test_function_source_pos();