This is an automated email from the ASF dual-hosted git repository.

xiazcy pushed a commit to branch 3.8-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/3.8-dev by this push:
     new 390ab7012a fix(gremlin-javascript): Reject out-of-range OffsetDateTime 
values (#3549)
390ab7012a is described below

commit 390ab7012ad6802c7e9c8aa69d2b795ace746f6f
Author: Yang Xia <[email protected]>
AuthorDate: Mon Jul 27 11:28:33 2026 -0700

    fix(gremlin-javascript): Reject out-of-range OffsetDateTime values (#3549)
    
    Validate the constructed Date in the GraphBinary and GraphSON 
OffsetDateTime readers and throw when it falls outside the range representable 
by a JavaScript Date, instead of silently returning an invalid Date.
    
    https://issues.apache.org/jira/browse/TINKERPOP-3276
    
    Assisted-by: Kiro:Claude-Opus-4.8
---
 CHANGELOG.asciidoc                                 |   2 +-
 .../binary/internals/OffsetDateTimeSerializer.js   |   9 ++
 .../lib/structure/io/type-serializers.js           |  10 +-
 .../graphbinary/OffsetDateTimeSerializer-test.js   | 118 +++++++++++++++++++++
 .../gremlin-javascript/test/unit/graphson-test.js  |  16 +++
 5 files changed, 153 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index d01a9fc15d..c4379896bf 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -32,7 +32,7 @@ This release also includes changes from prior 3.7.x releases.
 * Bumped Hadoop to 3.4.3 (and Kerby to 2.0.3) to enable `hadoop-gremlin` to 
build and run on Java 25.
 * Add missing `Configuring` interface to `GraphStepPlaceholder` and 
`VertexStepPlaceholder`
 * Fixed bug in `group()` value traversal where keys were retained with stale 
barrier state instead of being filtered when steps following a `Barrier` in the 
second `by()` produced no output (e.g. `by(values("age").fold().unfold())` or 
`by(__.out().fold().count(local).is(P.gt(0)))` for vertices with no out-edges).
-* Corrected numerous inaccuracies in the reference documentation, including 
wrong default values (connection pool sizes, buffer sizes, ports, timeouts), 
stale serializer class names, removed options documented as available, and 
broken code examples across the JVM, Python, `.NET`, Go, and JavaScript drivers.
+* Fixed bug in `gremlin-javascript` GraphBinary and GraphSON deserialization 
where `OffsetDateTime` values outside the JavaScript `Date` range were silently 
returned as invalid `Date` objects instead of failing deserialization.
 * Added a `propertyMap()` helper to view an element's properties as a map 
keyed by property key, on the `Element` structure API in `gremlin-core` 
(inherited by `Vertex`, `Edge`, and `VertexProperty`) and on `Vertex`, `Edge`, 
and `VertexProperty` in `gremlin-javascript`, `gremlin-python`, 
`gremlin-dotnet`, and `gremlin-go`.
 * Fixed a bug in `gremlin-go` where a `VertexProperty` deserialized from 
GraphBinary did not have its `Key` field populated, causing 
`Vertex.PropertyMap()` to group properties under an empty key.
 
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/OffsetDateTimeSerializer.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/OffsetDateTimeSerializer.js
index 30045ffd0f..0a30855d29 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/OffsetDateTimeSerializer.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/OffsetDateTimeSerializer.js
@@ -143,6 +143,15 @@ module.exports = class OffsetDateTimeSerializer {
       // use UTC time calculated with offset above
       const v = new Date(Date.UTC(year, month, date, h, m, s, ms));
 
+      // The GraphBinary DateTime/OffsetDateTime wire format can carry values 
(e.g. extreme years near
+      // +/-999999999) that fall outside the range representable by a 
JavaScript Date. In those cases
+      // Date.UTC(...) returns NaN and new Date(NaN) yields an invalid Date 
without throwing. Reject such
+      // values here so unsupported boundary date-times fail deserialization 
instead of silently producing
+      // an unusable Date instance.
+      if (Number.isNaN(v.getTime())) {
+        throw new Error('{value} is outside the range supported by JavaScript 
Date');
+      }
+
       return { v, len };
     } catch (err) {
       throw this.ioc.utils.des_error({ serializer: this, args: arguments, 
cursor, err });
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
index 7f5a392fd6..a3a683660c 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
@@ -103,7 +103,15 @@ class OffsetDateTimeSerializer extends TypeSerializer {
   }
 
   deserialize(obj) {
-    return new Date(obj[valueKey]);
+    // An OffsetDateTime value can carry a date-time (e.g. extreme years) that 
falls outside the range
+    // representable by a JavaScript Date. In those cases new Date(...) yields 
an invalid Date without
+    // throwing. Reject such values so unsupported boundary date-times fail 
deserialization instead of
+    // silently producing an unusable Date instance. Mirrors the GraphBinary 
OffsetDateTime reader.
+    const value = new Date(obj[valueKey]);
+    if (Number.isNaN(value.getTime())) {
+      throw new Error('OffsetDateTime value is outside the range supported by 
JavaScript Date');
+    }
+    return value;
   }
 
   canBeUsedFor(value) {
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/OffsetDateTimeSerializer-test.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/OffsetDateTimeSerializer-test.js
new file mode 100644
index 0000000000..f854fad351
--- /dev/null
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/OffsetDateTimeSerializer-test.js
@@ -0,0 +1,118 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+'use strict';
+
+const utils = require('./utils');
+const assert = require('assert');
+const ioc = require('../../../lib/structure/io/binary/GraphBinary');
+
+const { from, concat } = Buffer;
+
+const ID = 0x88; // OFFSETDATETIME
+
+describe('GraphBinary.OffsetDateTimeSerializer', () => {
+
+  const type_code = from([ID]);
+  const value_flag = from([0x00]);
+
+  const serializer = ioc.serializers[ID];
+
+  const cases = [
+    { v: undefined, fq: 1, b: [ID, 0x01], av: null },
+    { v: null,      fq: 1, b: [ID, 0x01] },
+
+    // year=2022(0x07e6), month=5, day=1, ns=0x000042c277bd8e00, offset=0
+    { v: new Date(1651436603000),
+      b: [0x00,0x00,0x07,0xe6, 0x05, 0x01, 
0x00,0x00,0x42,0xc2,0x77,0xbd,0x8e,0x00, 0x00,0x00,0x00,0x00] },
+
+    { des: 1, err: /buffer is missing/,         fq: 1, b: undefined },
+    { des: 1, err: /buffer is empty/,           fq: 1, b: [] },
+    { des: 1, err: /unexpected {type_code}/,    fq: 1, b: [ID - 1] },
+    { des: 1, err: /{value_flag} is missing/,   fq: 1, b: [ID] },
+    { des: 1, err: /unexpected {value_flag}/,   fq: 1, b: [ID, 0x02] },
+    { des: 1, err: /unexpected {value} length/, fq: 1, b: [ID, 0x00] },
+
+    // Boundary values that fall outside the JavaScript Date range 
(year=999999999=0x3B9AC9FF)
+    // must be rejected rather than silently deserialized into an invalid 
Date. See TINKERPOP-3276.
+    { des: 1, err: /outside the range supported by JavaScript Date/, fq: 0,
+      b: [0x3B,0x9A,0xC9,0xFF, 0x01, 0x01, 
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00] },
+    // year=-999999999=0xC4653601
+    { des: 1, err: /outside the range supported by JavaScript Date/, fq: 0,
+      b: [0xC4,0x65,0x36,0x01, 0x01, 0x01, 
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00] },
+  ];
+
+  describe('#serialize', () =>
+    cases
+    .filter(({ des }) => !des)
+    .forEach(({ v, fq, b }, i) => it(utils.ser_title({ i, v }), () => {
+      b = from(b);
+
+      if (fq !== undefined) {
+        assert.deepEqual(serializer.serialize(v, fq), b);
+        return;
+      }
+
+      assert.deepEqual(serializer.serialize(v, true),  concat([type_code, 
value_flag, b]));
+      assert.deepEqual(serializer.serialize(v, false), concat([b]));
+    }))
+  );
+
+  describe('#deserialize', () =>
+    cases.forEach(({ v, fq, b, av, err }, i) => it(utils.des_title({ i, b }), 
() => {
+      if (Array.isArray(b))
+        b = from(b);
+
+      if (err !== undefined) {
+        if (fq !== undefined)
+          assert.throws(() => serializer.deserialize(b, fq), { message: err });
+        else {
+          assert.throws(() => serializer.deserialize(concat([type_code, 
value_flag, b]), true),  { message: err });
+          assert.throws(() => serializer.deserialize(concat([b]), false), { 
message: err });
+        }
+        return;
+      }
+
+      if (av !== undefined)
+        v = av;
+      const len = b.length;
+
+      if (fq !== undefined) {
+        assert.deepStrictEqual(serializer.deserialize(b, fq), { v, len });
+        return;
+      }
+
+      assert.deepStrictEqual(serializer.deserialize(concat([type_code, 
value_flag, b]), true),  { v, len: len + 2 });
+      assert.deepStrictEqual(serializer.deserialize(concat([b]), false), { v, 
len: len + 0 });
+    }))
+  );
+
+  describe('#canBeUsedFor', () =>
+    [
+      { v: null,       e: false },
+      { v: undefined,  e: false },
+      { v: {},         e: false },
+      { v: [],         e: false },
+      { v: new Date(), e: true  },
+    ].forEach(({ v, e }, i) => it(utils.cbuf_title({ i, v }), () =>
+      assert.strictEqual(serializer.canBeUsedFor(v), e)
+    ))
+  );
+
+});
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js
index 7769306704..f968c35230 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js
@@ -90,6 +90,22 @@ describe('GraphSONReader', function () {
     const result = reader.read(obj);
     assert.ok(result instanceof Date);
   });
+  it('should parse OffsetDateTime', function() {
+    const obj = { "@type" : "gx:OffsetDateTime", "@value" : 
"2016-12-14T21:14:36.295Z" };
+    const reader = new GraphSONReader();
+    const result = reader.read(obj);
+    assert.ok(result instanceof Date);
+    assert.strictEqual(result.getTime(), 1481750076295);
+  });
+  it('should reject OffsetDateTime outside the JavaScript Date range', 
function() {
+    const reader = new GraphSONReader();
+    [
+      { "@type" : "gx:OffsetDateTime", "@value" : "+999999-01-01T00:00:00Z" },
+      { "@type" : "gx:OffsetDateTime", "@value" : "-999999-01-01T00:00:00Z" },
+    ].forEach(function (obj) {
+      assert.throws(() => reader.read(obj), /outside the range supported by 
JavaScript Date/);
+    });
+  });
   it('should parse vertices from GraphSON', function () {
     const obj = {
       
"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":1},"label":"person",

Reply via email to