X Tutup
The Wayback Machine - https://web.archive.org/web/20220713231653/https://github.com/nodejs/node/pull/42929
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

lib: make validateObject less affected by prototype tampering #42929

Merged

Conversation

aduh95
Copy link
Contributor

@aduh95 aduh95 commented Apr 30, 2022

No description provided.

@nodejs-github-bot nodejs-github-bot added the needs-ci label Apr 30, 2022
@aduh95
Copy link
Contributor Author

@aduh95 aduh95 commented May 10, 2022

Benchmark CI: https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1133/

No significant perf decrease or improvments
                                                                                               confidence improvement accuracy (*)    (**)   (***)
streams/creation.js kind='duplex' n=50000000                                                                  -0.43 %       ±2.61%  ±3.47%  ±4.52%
streams/creation.js kind='readable' n=50000000                                                                -0.44 %       ±1.20%  ±1.59%  ±2.08%
streams/creation.js kind='transform' n=50000000                                                                0.95 %       ±5.24%  ±6.97%  ±9.07%
streams/creation.js kind='writable' n=50000000                                                                 0.51 %       ±1.16%  ±1.55%  ±2.02%
streams/pipe.js n=5000000                                                                                     -0.62 %       ±1.35%  ±1.80%  ±2.34%
streams/pipe-object-mode.js n=5000000                                                                         -2.52 %       ±3.06%  ±4.07%  ±5.31%
streams/readable-async-iterator.js sync='no' n=100000                                                         -0.33 %       ±3.73%  ±4.97%  ±6.46%
streams/readable-async-iterator.js sync='yes' n=100000                                                         0.81 %       ±5.28%  ±7.03%  ±9.14%
streams/readable-bigread.js n=1000                                                                             2.74 %       ±4.13%  ±5.50%  ±7.15%
streams/readable-bigunevenread.js n=1000                                                                      -1.18 %       ±4.18%  ±5.56%  ±7.23%
streams/readable-boundaryread.js type='buffer' n=2000                                                         -0.43 %       ±2.51%  ±3.34%  ±4.35%
streams/readable-boundaryread.js type='string' n=2000                                                          1.71 %       ±3.90%  ±5.19%  ±6.76%
streams/readable-from.js n=10000000                                                                           -1.51 %       ±4.60%  ±6.12%  ±7.97%
streams/readable-readall.js n=5000                                                                            -0.29 %       ±2.65%  ±3.53%  ±4.59%
streams/readable-unevenread.js n=1000                                                                          0.45 %       ±3.41%  ±4.53%  ±5.90%
streams/writable-manywrites.js len=1024 callback='no' writev='no' sync='no' n=2000000                          0.35 %       ±2.96%  ±3.94%  ±5.13%
streams/writable-manywrites.js len=1024 callback='no' writev='no' sync='yes' n=2000000                        -5.22 %       ±8.01% ±10.66% ±13.88%
streams/writable-manywrites.js len=1024 callback='no' writev='yes' sync='no' n=2000000                         0.83 %       ±2.10%  ±2.79%  ±3.64%
streams/writable-manywrites.js len=1024 callback='no' writev='yes' sync='yes' n=2000000                 *     10.76 %       ±9.17% ±12.22% ±15.92%
streams/writable-manywrites.js len=1024 callback='yes' writev='no' sync='no' n=2000000                         0.85 %       ±3.03%  ±4.03%  ±5.25%
streams/writable-manywrites.js len=1024 callback='yes' writev='no' sync='yes' n=2000000                        0.18 %       ±5.96%  ±7.94% ±10.35%
streams/writable-manywrites.js len=1024 callback='yes' writev='yes' sync='no' n=2000000                       -2.84 %       ±4.41%  ±5.91%  ±7.80%
streams/writable-manywrites.js len=1024 callback='yes' writev='yes' sync='yes' n=2000000                       0.71 %       ±6.24%  ±8.30% ±10.80%
streams/writable-manywrites.js len=32768 callback='no' writev='no' sync='no' n=2000000                         2.94 %       ±3.74%  ±4.97%  ±6.47%
streams/writable-manywrites.js len=32768 callback='no' writev='no' sync='yes' n=2000000                        0.99 %       ±4.04%  ±5.40%  ±7.08%
streams/writable-manywrites.js len=32768 callback='no' writev='yes' sync='no' n=2000000                        1.35 %       ±3.66%  ±4.88%  ±6.35%
streams/writable-manywrites.js len=32768 callback='no' writev='yes' sync='yes' n=2000000                       1.65 %       ±2.03%  ±2.71%  ±3.55%
streams/writable-manywrites.js len=32768 callback='yes' writev='no' sync='no' n=2000000                       -1.63 %       ±6.15%  ±8.18% ±10.65%
streams/writable-manywrites.js len=32768 callback='yes' writev='no' sync='yes' n=2000000                      -0.13 %       ±3.49%  ±4.64%  ±6.04%
streams/writable-manywrites.js len=32768 callback='yes' writev='yes' sync='no' n=2000000                      -2.08 %       ±3.02%  ±4.02%  ±5.23%
streams/writable-manywrites.js len=32768 callback='yes' writev='yes' sync='yes' n=2000000                     -0.30 %       ±2.02%  ±2.69%  ±3.52%
util/format.js type='many-%' n=100000                                                                          1.93 %       ±4.65%  ±6.19%  ±8.05%
util/format.js type='no-replace-2' n=100000                                                                    0.59 %       ±6.03%  ±8.03% ±10.46%
util/format.js type='no-replace' n=100000                                                                     -0.75 %       ±3.48%  ±4.63%  ±6.02%
util/format.js type='number' n=100000                                                                          1.71 %       ±5.68%  ±7.56%  ±9.86%
util/format.js type='object-%s' n=100000                                                                       2.98 %       ±4.07%  ±5.47%  ±7.24%
util/format.js type='object-to-string' n=100000                                                               -4.02 %       ±5.41%  ±7.21%  ±9.42%
util/format.js type='only-objects' n=100000                                                                    0.28 %       ±1.58%  ±2.10%  ±2.73%
util/format.js type='replace-object' n=100000                                                                  0.81 %       ±5.98%  ±7.96% ±10.36%
util/format.js type='string-2' n=100000                                                                       -0.25 %       ±6.09%  ±8.11% ±10.55%
util/format.js type='string' n=100000                                                                         -2.08 %       ±6.51%  ±8.68% ±11.36%
util/format.js type='unknown' n=100000                                                                         3.21 %       ±4.90%  ±6.52%  ±8.49%
util/inspect-array.js type='denseArray' len=100000 n=500                                                       1.72 %       ±5.44%  ±7.25%  ±9.43%
util/inspect-array.js type='denseArray' len=100 n=500                                                          1.42 %       ±5.26%  ±7.01%  ±9.14%
util/inspect-array.js type='denseArray_showHidden' len=100000 n=500                                           -2.12 %       ±5.32%  ±7.07%  ±9.21%
util/inspect-array.js type='denseArray_showHidden' len=100 n=500                                               2.67 %       ±5.61%  ±7.48%  ±9.76%
util/inspect-array.js type='mixedArray' len=100000 n=500                                                       1.81 %       ±2.65%  ±3.52%  ±4.59%
util/inspect-array.js type='mixedArray' len=100 n=500                                                          2.59 %       ±7.38%  ±9.85% ±12.88%
util/inspect-array.js type='sparseArray' len=100000 n=500                                                     -0.78 %       ±3.76%  ±5.00%  ±6.51%
util/inspect-array.js type='sparseArray' len=100 n=500                                                         2.14 %       ±7.14%  ±9.50% ±12.37%
util/inspect.js option='colors' method='Array' n=20000                                                        -1.58 %       ±2.80%  ±3.72%  ±4.85%
util/inspect.js option='colors' method='Date' n=20000                                                         -3.05 %       ±6.64%  ±8.83% ±11.49%
util/inspect.js option='colors' method='Error' n=20000                                                         1.29 %       ±4.79%  ±6.38%  ±8.31%
util/inspect.js option='colors' method='Number' n=20000                                                       -0.56 %       ±5.27%  ±7.01%  ±9.14%
util/inspect.js option='colors' method='Object_deep_ln' n=20000                                                1.36 %       ±2.47%  ±3.30%  ±4.30%
util/inspect.js option='colors' method='Object_empty' n=20000                                                 -3.96 %       ±6.41%  ±8.54% ±11.12%
util/inspect.js option='colors' method='Object' n=20000                                                       -2.08 %       ±2.91%  ±3.87%  ±5.04%
util/inspect.js option='colors' method='Set' n=20000                                                          -1.02 %       ±4.98%  ±6.63%  ±8.64%
util/inspect.js option='colors' method='String_boxed' n=20000                                                  0.04 %       ±4.87%  ±6.49%  ±8.46%
util/inspect.js option='colors' method='String_complex' n=20000                                         *      7.75 %       ±7.65% ±10.17% ±13.24%
util/inspect.js option='colors' method='String' n=20000                                                        0.67 %       ±6.95%  ±9.25% ±12.04%
util/inspect.js option='colors' method='TypedArray_extra' n=20000                                             -2.24 %       ±2.76%  ±3.67%  ±4.79%
util/inspect.js option='colors' method='TypedArray' n=20000                                                   -2.07 %       ±3.27%  ±4.37%  ±5.74%
util/inspect.js option='none' method='Array' n=20000                                                           1.18 %       ±2.73%  ±3.66%  ±4.81%
util/inspect.js option='none' method='Date' n=20000                                                           -3.81 %       ±6.84%  ±9.10% ±11.85%
util/inspect.js option='none' method='Error' n=20000                                                          -0.05 %       ±5.82%  ±7.76% ±10.13%
util/inspect.js option='none' method='Number' n=20000                                                          2.42 %       ±6.19%  ±8.25% ±10.78%
util/inspect.js option='none' method='Object_deep_ln' n=20000                                                 -2.43 %       ±2.54%  ±3.38%  ±4.40%
util/inspect.js option='none' method='Object_empty' n=20000                                                   -5.94 %       ±6.43%  ±8.57% ±11.17%
util/inspect.js option='none' method='Object' n=20000                                                         -1.41 %       ±5.27%  ±7.03%  ±9.19%
util/inspect.js option='none' method='Set' n=20000                                                            -4.41 %       ±5.90%  ±7.86% ±10.25%
util/inspect.js option='none' method='String_boxed' n=20000                                                    2.38 %       ±5.66%  ±7.53%  ±9.80%
util/inspect.js option='none' method='String_complex' n=20000                                                  2.25 %       ±6.33%  ±8.43% ±10.99%
util/inspect.js option='none' method='String' n=20000                                                          0.86 %       ±8.26% ±11.03% ±14.42%
util/inspect.js option='none' method='TypedArray_extra' n=20000                                                0.03 %       ±5.00%  ±6.65%  ±8.66%
util/inspect.js option='none' method='TypedArray' n=20000                                                     -1.73 %       ±6.84%  ±9.10% ±11.85%
util/inspect.js option='showHidden' method='Array' n=20000                                                    -0.15 %       ±1.26%  ±1.68%  ±2.19%
util/inspect.js option='showHidden' method='Date' n=20000                                                     -0.31 %       ±5.39%  ±7.17%  ±9.33%
util/inspect.js option='showHidden' method='Error' n=20000                                                    -1.90 %       ±3.82%  ±5.09%  ±6.66%
util/inspect.js option='showHidden' method='Number' n=20000                                             *      6.32 %       ±4.99%  ±6.66%  ±8.73%
util/inspect.js option='showHidden' method='Object_deep_ln' n=20000                                           -2.68 %       ±2.73%  ±3.63%  ±4.72%
util/inspect.js option='showHidden' method='Object_empty' n=20000                                             -1.31 %       ±4.87%  ±6.49%  ±8.47%
util/inspect.js option='showHidden' method='Object' n=20000                                                   -0.21 %       ±4.24%  ±5.65%  ±7.35%
util/inspect.js option='showHidden' method='Set' n=20000                                                      -0.12 %       ±6.02%  ±8.01% ±10.43%
util/inspect.js option='showHidden' method='String_boxed' n=20000                                              4.07 %       ±5.82%  ±7.74% ±10.08%
util/inspect.js option='showHidden' method='String_complex' n=20000                                            0.10 %       ±6.04%  ±8.03% ±10.46%
util/inspect.js option='showHidden' method='String' n=20000                                                    1.87 %       ±5.68%  ±7.56%  ±9.86%
util/inspect.js option='showHidden' method='TypedArray_extra' n=20000                                   *     -2.58 %       ±2.56%  ±3.40%  ±4.43%
util/inspect.js option='showHidden' method='TypedArray' n=20000                                                0.37 %       ±2.65%  ±3.53%  ±4.59%
util/inspect-proxy.js isProxy=0 showProxy=0 n=100000                                                          -2.69 %       ±2.83%  ±3.78%  ±4.92%
util/inspect-proxy.js isProxy=0 showProxy=1 n=100000                                                           0.86 %       ±2.08%  ±2.78%  ±3.63%
util/inspect-proxy.js isProxy=1 showProxy=0 n=100000                                                           1.65 %       ±4.76%  ±6.40%  ±8.46%
util/inspect-proxy.js isProxy=1 showProxy=1 n=100000                                                    *     -2.96 %       ±2.49%  ±3.31%  ±4.31%
util/normalize-encoding.js n=100000 input=''                                                                  -3.03 %       ±4.80%  ±6.40%  ±8.35%
util/normalize-encoding.js n=100000 input='base64'                                                             2.97 %       ±5.44%  ±7.23%  ±9.42%
util/normalize-encoding.js n=100000 input='BASE64'                                                            -4.04 %       ±6.24%  ±8.30% ±10.81%
util/normalize-encoding.js n=100000 input='binary'                                                            -4.11 %       ±7.46%  ±9.94% ±12.94%
util/normalize-encoding.js n=100000 input='BINARY'                                                             5.95 %       ±7.19%  ±9.58% ±12.48%
util/normalize-encoding.js n=100000 input='foo'                                                               -0.72 %       ±5.24%  ±6.99%  ±9.14%
util/normalize-encoding.js n=100000 input='group_common'                                                      -2.37 %       ±4.66%  ±6.20%  ±8.09%
util/normalize-encoding.js n=100000 input='group_misc'                                                         0.63 %       ±6.99%  ±9.30% ±12.12%
util/normalize-encoding.js n=100000 input='group_uncommon'                                              *     -6.45 %       ±5.81%  ±7.74% ±10.09%
util/normalize-encoding.js n=100000 input='group_upper'                                                       -1.35 %       ±4.68%  ±6.23%  ±8.11%
util/normalize-encoding.js n=100000 input='hex'                                                               -1.50 %       ±5.26%  ±7.00%  ±9.11%
util/normalize-encoding.js n=100000 input='HEX'                                                               -0.14 %       ±4.89%  ±6.51%  ±8.47%
util/normalize-encoding.js n=100000 input='latin1'                                                            -0.98 %       ±7.20%  ±9.58% ±12.48%
util/normalize-encoding.js n=100000 input='ucs2'                                                               2.36 %       ±6.15%  ±8.21% ±10.74%
util/normalize-encoding.js n=100000 input='UCS2'                                                               3.69 %       ±5.62%  ±7.52%  ±9.87%
util/normalize-encoding.js n=100000 input='undefined'                                                         -0.44 %       ±5.45%  ±7.26%  ±9.45%
util/normalize-encoding.js n=100000 input='utf16le'                                                            3.30 %       ±6.64%  ±8.85% ±11.53%
util/normalize-encoding.js n=100000 input='UTF16LE'                                                            2.73 %       ±7.68% ±10.24% ±13.35%
util/normalize-encoding.js n=100000 input='utf-8'                                                              3.56 %       ±5.97%  ±7.96% ±10.40%
util/normalize-encoding.js n=100000 input='utf8'                                                               1.63 %       ±6.06%  ±8.07% ±10.51%
util/normalize-encoding.js n=100000 input='Utf8'                                                               2.39 %       ±6.52%  ±8.70% ±11.36%
util/normalize-encoding.js n=100000 input='UTF-8'                                                             -2.80 %       ±5.65%  ±7.53%  ±9.83%
util/normalize-encoding.js n=100000 input='UTF8'                                                               5.86 %       ±7.94% ±10.60% ±13.85%
util/priority-queue.js n=100000                                                                               -0.62 %       ±4.04%  ±5.37%  ±7.00%
util/splice-one.js size=100 pos='end' n=100000                                                                -1.93 %       ±6.97%  ±9.27% ±12.07%
util/splice-one.js size=100 pos='middle' n=100000                                                              0.60 %       ±5.40%  ±7.19%  ±9.39%
util/splice-one.js size=100 pos='start' n=100000                                                              -4.81 %       ±6.55%  ±8.72% ±11.37%
util/splice-one.js size=10 pos='end' n=100000                                                                 -2.34 %       ±5.64%  ±7.52%  ±9.83%
util/splice-one.js size=10 pos='middle' n=100000                                                              -1.11 %       ±5.75%  ±7.65%  ±9.96%
util/splice-one.js size=10 pos='start' n=100000                                                               -3.52 %       ±4.55%  ±6.06%  ±7.89%
util/splice-one.js size=500 pos='end' n=100000                                                                -4.25 %       ±5.41%  ±7.21%  ±9.39%
util/splice-one.js size=500 pos='middle' n=100000                                                             -2.74 %       ±3.61%  ±4.80%  ±6.25%
util/splice-one.js size=500 pos='start' n=100000                                                               0.66 %       ±2.61%  ±3.47%  ±4.53%
util/to-usv-string.js size=100 n=100000                                                                        0.31 %       ±2.00%  ±2.66%  ±3.47%
util/to-usv-string.js size=10 n=100000                                                                         1.62 %       ±5.06%  ±6.73%  ±8.77%
util/to-usv-string.js size=500 n=100000                                                                       -1.04 %       ±1.96%  ±2.60%  ±3.39%
util/type-check.js n=100000 argument='false-object' version='js' type='ArrayBufferView'                       -0.76 %       ±7.91% ±10.53% ±13.71%
util/type-check.js n=100000 argument='false-object' version='js' type='TypedArray'                             3.09 %       ±6.64%  ±8.84% ±11.52%
util/type-check.js n=100000 argument='false-object' version='js' type='Uint8Array'                             0.41 %       ±5.57%  ±7.43%  ±9.72%
util/type-check.js n=100000 argument='false-object' version='native' type='ArrayBufferView'                   -2.85 %       ±6.19%  ±8.23% ±10.72%
util/type-check.js n=100000 argument='false-object' version='native' type='TypedArray'                        -4.50 %       ±5.48%  ±7.29%  ±9.49%
util/type-check.js n=100000 argument='false-object' version='native' type='Uint8Array'                         3.20 %       ±5.04%  ±6.73%  ±8.82%
util/type-check.js n=100000 argument='false-primitive' version='js' type='ArrayBufferView'                    -0.68 %       ±5.54%  ±7.37%  ±9.59%
util/type-check.js n=100000 argument='false-primitive' version='js' type='TypedArray'                         -2.73 %       ±6.15%  ±8.20% ±10.72%
util/type-check.js n=100000 argument='false-primitive' version='js' type='Uint8Array'                         -1.26 %       ±5.98%  ±7.95% ±10.35%
util/type-check.js n=100000 argument='false-primitive' version='native' type='ArrayBufferView'                 0.16 %       ±7.01%  ±9.33% ±12.15%
util/type-check.js n=100000 argument='false-primitive' version='native' type='TypedArray'                      3.00 %      ±10.64% ±14.24% ±18.69%
util/type-check.js n=100000 argument='false-primitive' version='native' type='Uint8Array'                      1.52 %       ±4.52%  ±6.02%  ±7.83%
util/type-check.js n=100000 argument='true' version='js' type='ArrayBufferView'                               -1.49 %       ±6.99%  ±9.30% ±12.10%
util/type-check.js n=100000 argument='true' version='js' type='TypedArray'                              *     -7.12 %       ±6.25%  ±8.34% ±10.90%
util/type-check.js n=100000 argument='true' version='js' type='Uint8Array'                              *      5.20 %       ±5.05%  ±6.75%  ±8.83%
util/type-check.js n=100000 argument='true' version='native' type='ArrayBufferView'                           -1.35 %       ±4.97%  ±6.62%  ±8.63%
util/type-check.js n=100000 argument='true' version='native' type='TypedArray'                                 2.60 %       ±4.57%  ±6.09%  ±7.92%
util/type-check.js n=100000 argument='true' version='native' type='Uint8Array'                                -1.27 %       ±5.08%  ±6.76%  ±8.80%

Be aware that when doing many comparisons the risk of a false-positive
result increases. In this case, there are 147 comparisons, you can thus
expect the following amount of false-positive results:
  7.35 false positives, when considering a   5% risk acceptance (*, **, ***),
  1.47 false positives, when considering a   1% risk acceptance (**, ***),
  0.15 false positives, when considering a 0.1% risk acceptance (***)

@aduh95
Copy link
Contributor Author

@aduh95 aduh95 commented Jun 23, 2022

@nodejs/tsc can this get some reviews please?

@Trott Trott added the request-ci label Jun 24, 2022
@github-actions github-actions bot removed the request-ci label Jun 24, 2022
@nodejs-github-bot
Copy link
Contributor

@nodejs-github-bot nodejs-github-bot commented Jun 24, 2022

@nodejs-github-bot
Copy link
Contributor

@nodejs-github-bot nodejs-github-bot commented Jun 24, 2022

@@ -140,6 +141,10 @@ function validateBoolean(value, name) {
throw new ERR_INVALID_ARG_TYPE(name, 'boolean', value);
}

function getOwnPropertyValueOrDefault(options, key, defaultValue) {
return options == null || !ObjectPrototypeHasOwnProperty(options, key) ? defaultValue : options[key];
Copy link
Member

@F3n67u F3n67u Jun 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@aduh95 aduh95 Jun 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s not available in primordials because it can be disabled with a runtime flag

V(harmony_object_has_own, "harmony Object.hasOwn") \

Copy link
Member

@F3n67u F3n67u Jun 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info.

@aduh95 aduh95 force-pushed the validateObject-prototype-tampering branch from a05b2c6 to 5ddd351 Compare Jul 9, 2022
Copy link
Member

@tniessen tniessen left a comment

Is the rule of thumb going to be that we pass objects with a null prototype to V8 built-ins, but that we don't use null prototypes and instead only check own properties for our own utility functions?

@aduh95
Copy link
Contributor Author

@aduh95 aduh95 commented Jul 9, 2022

I think for internals, it's probably simpler to only check own properties because we never use inheritance on validateObject options anyway. For public APIs, I guess we don't want to check for own properties to allow the use of prototype inheritance (since that's what most ECMAScript builtins are doing) but I think we are not very consistent with that (see #43401).

Copy link
Member

@tniessen tniessen left a comment

LGTM as long as we don't start passing objects with null prototypes to such functions.

@aduh95 aduh95 added request-ci author ready labels Jul 10, 2022
@github-actions github-actions bot removed the request-ci label Jul 10, 2022
@nodejs-github-bot
Copy link
Contributor

@nodejs-github-bot nodejs-github-bot commented Jul 10, 2022

@nodejs-github-bot
Copy link
Contributor

@nodejs-github-bot nodejs-github-bot commented Jul 10, 2022

@nodejs-github-bot
Copy link
Contributor

@nodejs-github-bot nodejs-github-bot commented Jul 10, 2022

@aduh95 aduh95 added the commit-queue label Jul 11, 2022
@nodejs-github-bot nodejs-github-bot removed the commit-queue label Jul 11, 2022
@nodejs-github-bot nodejs-github-bot merged commit b4a7d9f into nodejs:main Jul 11, 2022
52 checks passed
@nodejs-github-bot
Copy link
Contributor

@nodejs-github-bot nodejs-github-bot commented Jul 11, 2022

Landed in b4a7d9f

@aduh95 aduh95 deleted the validateObject-prototype-tampering branch Jul 11, 2022
targos pushed a commit that referenced this issue Jul 12, 2022
PR-URL: #42929
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready needs-ci review wanted
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants
X Tutup