Merge pull request #355 from ripple/validate-uint-hex-input

Validate UInt* hex input
This commit is contained in:
sublimator
2015-06-22 13:59:17 +07:00
4 changed files with 1831 additions and 54 deletions

View File

@@ -54,7 +54,7 @@ SerializedObject.from_json = function(obj_) {
// Create a copy of the object so we don't modify it
const obj = extend(true, {}, obj_);
let so = new SerializedObject();
const so = new SerializedObject();
let typedef;
if (typeof obj.TransactionType === 'number') {
@@ -216,7 +216,7 @@ SerializedObject.prototype.to_json = function() {
this.resetPointer();
let output = { };
const output = { };
while (this.pointer < this.buffer.length) {
const key_and_value = stypes.parse(this);
@@ -265,10 +265,10 @@ SerializedObject.jsonify_structure = function(structure, field_name) {
// new Array or Object
output = new structure.constructor();
let keys = Object.keys(structure);
const keys = Object.keys(structure);
for (let i = 0, l = keys.length; i < l; i++) {
let key = keys[i];
const key = keys[i];
output[key] = SerializedObject.jsonify_structure(structure[key], key);
}
}
@@ -297,7 +297,7 @@ SerializedObject.prototype.serialize = function(typedef, obj) {
};
SerializedObject.prototype.hash = function(prefix) {
let sign_buffer = new SerializedObject();
const sign_buffer = new SerializedObject();
// Add hashing prefix
if (typeof prefix !== 'undefined') {
@@ -310,7 +310,7 @@ SerializedObject.prototype.hash = function(prefix) {
const bits = sjcl.codec.bytes.toBits(sign_buffer.buffer);
const sha512hex = sjcl.codec.hex.fromBits(sjcl.hash.sha512.hash(bits));
return UInt256.from_hex(sha512hex.substr(0, 64));
return UInt256.from_hex(sha512hex.substr(0, 64).toUpperCase());
};
// DEPRECATED
@@ -334,7 +334,7 @@ SerializedObject.prototype.serialize_field = function(spec, obj) {
};
SerializedObject.get_field_header = function(type_id, field_id) {
let buffer = [0];
const buffer = [0];
if (type_id > 0xF) {
buffer.push(type_id & 0xFF);

View File

@@ -2,8 +2,10 @@
/*eslint new-cap: 1*/
var utils = require('./utils');
var sjcl = utils.sjcl;
const assert = require('assert');
const lodash = require('lodash');
const utils = require('./utils');
const sjcl = utils.sjcl;
//
// Abstract UInt class
@@ -14,7 +16,6 @@ var sjcl = utils.sjcl;
function UInt() {
// Internal form: NaN or sjcl.bn
this._value = NaN;
this._update();
}
UInt.json_rewrite = function(j, opts) {
@@ -26,6 +27,7 @@ UInt.from_generic = function(j) {
if (j instanceof this) {
return j.clone();
}
return (new this()).parse_generic(j);
};
@@ -34,6 +36,7 @@ UInt.from_hex = function(j) {
if (j instanceof this) {
return j.clone();
}
return (new this()).parse_hex(j);
};
@@ -42,6 +45,7 @@ UInt.from_json = function(j) {
if (j instanceof this) {
return j.clone();
}
return (new this()).parse_json(j);
};
@@ -50,6 +54,7 @@ UInt.from_bits = function(j) {
if (j instanceof this) {
return j.clone();
}
return (new this()).parse_bits(j);
};
@@ -58,6 +63,7 @@ UInt.from_bytes = function(j) {
if (j instanceof this) {
return j.clone();
}
return (new this()).parse_bytes(j);
};
@@ -66,6 +72,7 @@ UInt.from_bn = function(j) {
if (j instanceof this) {
return j.clone();
}
return (new this()).parse_bn(j);
};
@@ -74,6 +81,7 @@ UInt.from_number = function(j) {
if (j instanceof this) {
return j.clone();
}
return (new this()).parse_number(j);
};
@@ -130,34 +138,46 @@ UInt.prototype._update = function() {
// value = NaN on error.
UInt.prototype.parse_generic = function(j) {
const subclass = this.constructor;
assert(typeof subclass.width === 'number', 'UInt missing width');
this._value = NaN;
switch (j) {
case undefined:
case '0':
case this.constructor.STR_ZERO:
case this.constructor.ACCOUNT_ZERO:
case this.constructor.HEX_ZERO:
case subclass.STR_ZERO:
case subclass.ACCOUNT_ZERO:
case subclass.HEX_ZERO:
this._value = new sjcl.bn(0);
break;
case '1':
case this.constructor.STR_ONE:
case this.constructor.ACCOUNT_ONE:
case this.constructor.HEX_ONE:
case subclass.STR_ONE:
case subclass.ACCOUNT_ONE:
case subclass.HEX_ONE:
this._value = new sjcl.bn(1);
break;
default:
if (typeof j !== 'string') {
this._value = NaN;
} else if (this.constructor.width === j.length) {
var hex = utils.arrayToHex(utils.stringToArray(j));
this._value = new sjcl.bn(hex, 16);
} else if ((this.constructor.width * 2) === j.length) {
// XXX Check char set!
this._value = new sjcl.bn(j, 16);
} else {
this._value = NaN;
if (lodash.isString(j)) {
switch (j.length) {
case subclass.width:
const hex = utils.arrayToHex(utils.stringToArray(j));
this._value = new sjcl.bn(hex, 16);
break;
case subclass.width * 2:
// Assume hex, check char set
this.parse_hex(j);
break;
}
} else if (lodash.isNumber(j)) {
this.parse_number(j);
} else if (lodash.isArray(j)) {
// Assume bytes array
this.parse_bytes(j);
}
}
this._update();
@@ -166,7 +186,7 @@ UInt.prototype.parse_generic = function(j) {
};
UInt.prototype.parse_hex = function(j) {
if (typeof j === 'string' && j.length === (this.constructor.width * 2)) {
if (new RegExp(`^[0-9A-Fa-f]{${this.constructor.width * 2}}$`).test(j)) {
this._value = new sjcl.bn(j, 16);
} else {
this._value = NaN;
@@ -178,12 +198,12 @@ UInt.prototype.parse_hex = function(j) {
};
UInt.prototype.parse_bits = function(j) {
if (sjcl.bitArray.bitLength(j) !== this.constructor.width * 8) {
this._value = NaN;
} else {
if (sjcl.bitArray.bitLength(j) === this.constructor.width * 8) {
this._value = sjcl.bn.fromBits(j);
// var bytes = sjcl.codec.bytes.fromBits(j);
// let bytes = sjcl.codec.bytes.fromBits(j);
// this.parse_bytes(bytes);
} else {
this._value = NaN;
}
this._update();
@@ -193,11 +213,11 @@ UInt.prototype.parse_bits = function(j) {
UInt.prototype.parse_bytes = function(j) {
if (!Array.isArray(j) || j.length !== this.constructor.width) {
this._value = NaN;
} else {
var bits = sjcl.codec.bytes.toBits(j);
if (Array.isArray(j) && j.length === this.constructor.width) {
const bits = sjcl.codec.bytes.toBits(j);
this._value = sjcl.bn.fromBits(bits);
} else {
this._value = NaN;
}
this._update();
@@ -209,8 +229,7 @@ UInt.prototype.parse_bytes = function(j) {
UInt.prototype.parse_json = UInt.prototype.parse_hex;
UInt.prototype.parse_bn = function(j) {
if ((j instanceof sjcl.bn) &&
j.bitLength() <= this.constructor.width * 8) {
if ((j instanceof sjcl.bn) && j.bitLength() <= this.constructor.width * 8) {
this._value = new sjcl.bn(j);
} else {
this._value = NaN;
@@ -238,6 +257,7 @@ UInt.prototype.to_bytes = function() {
if (!this.is_valid()) {
return null;
}
return sjcl.codec.bytes.fromBits(this.to_bits());
};
@@ -245,6 +265,7 @@ UInt.prototype.to_hex = function() {
if (!this.is_valid()) {
return null;
}
return sjcl.codec.hex.fromBits(this.to_bits()).toUpperCase();
};
@@ -263,7 +284,7 @@ UInt.prototype.to_bn = function() {
return null;
}
var bits = this.to_bits();
const bits = this.to_bits();
return sjcl.bn.fromBits(bits);
};

1675
test/fixtures/uint.json vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,104 @@
var assert = require('assert');
var UInt128 = require('ripple-lib').UInt128;
'use strict';
describe('UInt', function() {
describe('128', function() {
describe('#parse_number', function () {
it('should create 00000000000000000000000000000000 when called with 0', function () {
var val = UInt128.from_number(0);
assert.strictEqual(val.to_hex(), '00000000000000000000000000000000');
/* eslint-disable max-len */
const assert = require('assert-diff');
const lodash = require('lodash');
const ripple = require('ripple-lib');
const fixtures = require('./fixtures/uint');
function resultError(test, result) {
function type(e) {
return Object.prototype.toString.call(e);
}
return `Expected ${type(test.input)}: ${test.input} to yield ${type(test.expected)}: ${test.expected === 'null' ? NaN : test.expected}. Actual: ${type(result)}: ${result}`;
}
function makeTests(uIntType) {
describe(uIntType, function() {
const rippleType = ripple[uIntType];
const tests = fixtures[uIntType];
it('from_json().to_json()', function() {
tests['from_json().to_json()'].forEach(function(test) {
let result = rippleType.from_json(test.input);
assert.strictEqual(result.is_valid(), String(test.expected) !== 'null', `Validity check failed: ${test.input}`);
result = result.to_json();
if (test.expected === 'null') {
// XXX
// UInt160.to_json() returns NaN rather than null if input is invalid
assert.strictEqual(lodash.isNaN(result), true, resultError(test, result));
} else {
assert.strictEqual(result, test.expected, resultError(test, result));
}
});
it('should create 00000000000000000000000000000001 when called with 1', function () {
var val = UInt128.from_number(1);
assert.strictEqual(val.to_hex(), '00000000000000000000000000000001');
});
it('from_json().to_bytes()', function() {
tests['from_json().to_bytes()'].forEach(function(test) {
const result = rippleType.from_json(test.input);
assert.strictEqual(result.is_valid(), String(test.expected) !== 'null', `Validity check failed: ${test.input}`);
assert.deepEqual(result.to_bytes(), test.expected, resultError(test, result));
});
it('should create 000000000000000000000000FFFFFFFF when called with 0xFFFFFFFF', function () {
var val = UInt128.from_number(0xFFFFFFFF);
assert.strictEqual(val.to_hex(), '000000000000000000000000FFFFFFFF');
});
it('from_number().to_json()', function() {
tests['from_number().to_json()'].forEach(function(test) {
let result = rippleType.from_number(test.input);
assert.strictEqual(result.is_valid(), String(test.expected) !== 'null', `Validity check failed: ${test.input}`);
result = result.to_json();
if (test.expected === 'null') {
// XXX
// UInt160.to_json() returns NaN rather than null if input is invalid
assert.strictEqual(lodash.isNaN(result), true, resultError(test, result));
} else {
assert.strictEqual(result, test.expected, resultError(test, result));
}
});
});
it('from_number().to_hex()', function() {
tests['from_number().to_hex()'].forEach(function(test) {
const result = rippleType.from_number(test.input);
assert.strictEqual(result.is_valid(), String(test.expected) !== 'null', `Validity check failed: ${test.input}`);
assert.strictEqual(result.to_hex(), test.expected, resultError(test, result));
});
});
it('from_generic().to_*()', function() {
tests['from_generic().to_*()'].forEach(function(test) {
let result = rippleType.from_generic(test.input);
switch (test.input) {
// XXX
// from_generic() accepts these as "zero"
case 0:
case '0':
case undefined:
switch (test.outputMethod) {
case 'to_bytes':
test.expected = Array(rippleType.width).fill(0);
break;
case 'to_json':
case 'to_hex':
test.expected = Array(rippleType.width * 2).fill(0).join('');
break;
}
}
assert.strictEqual(result.is_valid(), String(test.expected) !== 'null',
`Validity check failed: ${test.input} > ${test.expected}`);
result = result[test.outputMethod]();
if (test.expected === 'null') {
// XXX
// UInt160.to_json() returns NaN rather than null if input is invalid
assert.strictEqual(lodash.isNaN(result), true, resultError(test, result));
} else {
assert.deepEqual(result, test.expected, resultError(test, result));
}
});
});
});
});
}
// vim:sw=2:sts=2:ts=8:et
['UInt128', 'UInt160', 'UInt256'].forEach(makeTests);