Skip to content

Commit fd4ffb7

Browse files
author
platfowner
authored
Merge pull request #38 from ainblockchain/feature/platfowner/valid_path
Feature/platfowner/valid path
2 parents 19ebf7b + fc60eed commit fd4ffb7

File tree

7 files changed

+280
-709
lines changed

7 files changed

+280
-709
lines changed

chain-util.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ class ChainUtil {
6565
if (!path) {
6666
return [];
6767
}
68-
path = path.replace(/^"(.*)"$/, '$1');
6968
return path.split('/').filter((node) => {
7069
return !!node;
7170
});
@@ -75,7 +74,15 @@ class ChainUtil {
7574
if (!Array.isArray(parsedPath) || !parsedPath.length) {
7675
return '/';
7776
}
78-
return (parsedPath[0].startsWith('/') ? '' : '/') + parsedPath.join('/');
77+
let formatted = '';
78+
for (const label of parsedPath) {
79+
if (ChainUtil.isString(label)) {
80+
formatted += '/' + label;
81+
} else {
82+
formatted += '/' + JSON.stringify(label);
83+
}
84+
}
85+
return (formatted.startsWith('/') ? '' : '/') + formatted;
7986
}
8087

8188
static transactionFailed(response) {

db/index.js

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const ChainUtil = require('../chain-util');
77
const Transaction = require('../tx-pool/transaction');
88
const StateNode = require('./state-node');
99
const {
10+
isValidPathForStates,
1011
isValidJsObjectForStates,
1112
jsObjectToStateTree,
1213
stateTreeToJsObject,
@@ -218,11 +219,15 @@ class DB {
218219
// TODO(seo): Consider making set operation and native function run tightly bound, i.e., revert
219220
// the former if the latter fails.
220221
setValue(valuePath, value, address, timestamp, transaction) {
221-
const {isValid, invalidPath} = isValidJsObjectForStates(value);
222-
if (!isValid) {
223-
return {code: 6, error_message: `Invalid object for states: ${invalidPath}`};
222+
const isValidObj = isValidJsObjectForStates(value);
223+
if (!isValidObj.isValid) {
224+
return {code: 6, error_message: `Invalid object for states: ${isValidObj.invalidPath}`};
224225
}
225226
const parsedPath = ChainUtil.parsePath(valuePath);
227+
const isValidPath = isValidPathForStates(parsedPath);
228+
if (!isValidPath.isValid) {
229+
return {code: 7, error_message: `Invalid path: ${isValidPath.invalidPath}`};
230+
}
226231
if (!this.getPermissionForValue(parsedPath, value, address, timestamp)) {
227232
return {code: 2, error_message: `No .write permission on: ${valuePath}`};
228233
}
@@ -258,11 +263,15 @@ class DB {
258263
}
259264

260265
setFunction(functionPath, functionInfo, address) {
261-
const {isValid, invalidPath} = isValidJsObjectForStates(functionInfo);
262-
if (!isValid) {
263-
return {code: 6, error_message: `Invalid object for states: ${invalidPath}`};
266+
const isValidObj = isValidJsObjectForStates(functionInfo);
267+
if (!isValidObj.isValid) {
268+
return {code: 6, error_message: `Invalid object for states: ${isValidObj.invalidPath}`};
264269
}
265270
const parsedPath = ChainUtil.parsePath(functionPath);
271+
const isValidPath = isValidPathForStates(parsedPath);
272+
if (!isValidPath.isValid) {
273+
return {code: 7, error_message: `Invalid path: ${isValidPath.invalidPath}`};
274+
}
266275
if (!this.getPermissionForFunction(parsedPath, address)) {
267276
return {code: 3, error_message: `No write_function permission on: ${functionPath}`};
268277
}
@@ -276,11 +285,15 @@ class DB {
276285
// TODO(seo): Add rule config sanitization logic (e.g. dup path variables,
277286
// multiple path variables).
278287
setRule(rulePath, rule, address) {
279-
const {isValid, invalidPath} = isValidJsObjectForStates(rule);
280-
if (!isValid) {
281-
return {code: 6, error_message: `Invalid object for states: ${invalidPath}`};
288+
const isValidObj = isValidJsObjectForStates(rule);
289+
if (!isValidObj.isValid) {
290+
return {code: 6, error_message: `Invalid object for states: ${isValidObj.invalidPath}`};
282291
}
283292
const parsedPath = ChainUtil.parsePath(rulePath);
293+
const isValidPath = isValidPathForStates(parsedPath);
294+
if (!isValidPath.isValid) {
295+
return {code: 7, error_message: `Invalid path: ${isValidPath.invalidPath}`};
296+
}
284297
if (!this.getPermissionForRule(parsedPath, address)) {
285298
return {code: 3, error_message: `No write_rule permission on: ${rulePath}`};
286299
}
@@ -292,11 +305,15 @@ class DB {
292305

293306
// TODO(seo): Add owner config sanitization logic.
294307
setOwner(ownerPath, owner, address) {
295-
const {isValid, invalidPath} = isValidJsObjectForStates(owner);
296-
if (!isValid) {
297-
return {code: 6, error_message: `Invalid object for states: ${invalidPath}`};
308+
const isValidObj = isValidJsObjectForStates(owner);
309+
if (!isValidObj.isValid) {
310+
return {code: 6, error_message: `Invalid object for states: ${isValidObj.invalidPath}`};
298311
}
299312
const parsedPath = ChainUtil.parsePath(ownerPath);
313+
const isValidPath = isValidPathForStates(parsedPath);
314+
if (!isValidPath.isValid) {
315+
return {code: 7, error_message: `Invalid path: ${isValidPath.invalidPath}`};
316+
}
300317
if (!this.getPermissionForOwner(parsedPath, address)) {
301318
return {code: 4, error_message: `No write_owner or branch_owner permission on: ${ownerPath}`};
302319
}

db/state-util.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
11
const StateNode = require('./state-node');
22
const ChainUtil = require('../chain-util');
33

4+
function hasReservedChar(label) {
5+
const pathReservedRegex = /[\/\.\*\$#\{\}\[\]\x00-\x1F\x7F]/gm;
6+
return ChainUtil.isString(label) ? pathReservedRegex.test(label) : false;
7+
}
8+
9+
function isValidPathForStates(fullPath) {
10+
let isValid = true;
11+
const path = [];
12+
for (const label of fullPath) {
13+
path.push(label);
14+
if (ChainUtil.isString(label)) {
15+
if (label === '' || hasReservedChar(label)) {
16+
isValid = false;
17+
break;
18+
}
19+
} else {
20+
isValid = false;
21+
break;
22+
}
23+
}
24+
return { isValid, invalidPath: isValid ? '' : ChainUtil.formatPath(path) };
25+
}
26+
427
function isValidJsObjectForStatesRecursive(obj, path) {
528
if (ChainUtil.isDict(obj)) {
629
if (ChainUtil.isEmptyNode(obj)) {
@@ -79,6 +102,8 @@ function makeCopyOfStateTree(root) {
79102
}
80103

81104
module.exports = {
105+
hasReservedChar,
106+
isValidPathForStates,
82107
isValidJsObjectForStates,
83108
jsObjectToStateTree,
84109
stateTreeToJsObject,

test/chain-util.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ describe("ChainUtil", () => {
1010
expect(ChainUtil.numberOrZero(undefined)).to.equal(0);
1111
expect(ChainUtil.numberOrZero(Infinity)).to.equal(0);
1212
expect(ChainUtil.numberOrZero(NaN)).to.equal(0);
13+
expect(ChainUtil.numberOrZero(true)).to.equal(0);
14+
expect(ChainUtil.numberOrZero(false)).to.equal(0);
1315
expect(ChainUtil.numberOrZero('')).to.equal(0);
1416
expect(ChainUtil.numberOrZero('abc')).to.equal(0);
1517
expect(ChainUtil.numberOrZero({})).to.equal(0);
@@ -41,6 +43,22 @@ describe("ChainUtil", () => {
4143
})
4244

4345
describe("formatPath", () => {
46+
it("when abnormal input", () => {
47+
assert.deepEqual(ChainUtil.formatPath([null]), '/null');
48+
assert.deepEqual(ChainUtil.formatPath([undefined]), '/undefined');
49+
assert.deepEqual(ChainUtil.formatPath([Infinity]), '/null');
50+
assert.deepEqual(ChainUtil.formatPath([NaN]), '/null');
51+
assert.deepEqual(ChainUtil.formatPath([true]), '/true');
52+
assert.deepEqual(ChainUtil.formatPath([false]), '/false');
53+
assert.deepEqual(ChainUtil.formatPath([0]), '/0');
54+
assert.deepEqual(ChainUtil.formatPath(['']), '/');
55+
assert.deepEqual(ChainUtil.formatPath(['', '', '']), '///');
56+
assert.deepEqual(ChainUtil.formatPath([{}]), '/{}');
57+
assert.deepEqual(ChainUtil.formatPath([{a: 'A'}]), '/{"a":"A"}');
58+
assert.deepEqual(ChainUtil.formatPath([[]]), '/[]');
59+
assert.deepEqual(ChainUtil.formatPath([['a']]), '/["a"]');
60+
})
61+
4462
it("when normal input", () => {
4563
assert.deepEqual(ChainUtil.formatPath(['a', 'b', 'c']), '/a/b/c');
4664
})

test/db.test.js

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,7 @@ describe("DB operations", () => {
899899

900900
it("when creating new path in database", () => {
901901
const newValue = 12345
902-
node.db.setValue("test/new/unchartered/nested/path", newValue)
902+
expect(node.db.setValue("test/new/unchartered/nested/path", newValue)).to.equal(true)
903903
expect(node.db.getValue("test/new/unchartered/nested/path")).to.equal(newValue)
904904
})
905905

@@ -910,6 +910,53 @@ describe("DB operations", () => {
910910
});
911911
expect(node.db.getValue("test/unchartered/nested/path2")).to.equal(null)
912912
})
913+
914+
it("when writing with invalid path", () => {
915+
assert.deepEqual(node.db.setValue("test/new/unchartered/nested/.", 12345), {
916+
"code": 7,
917+
"error_message": "Invalid path: /test/new/unchartered/nested/."
918+
});
919+
assert.deepEqual(node.db.setValue("test/new/unchartered/nested/*", 12345), {
920+
"code": 7,
921+
"error_message": "Invalid path: /test/new/unchartered/nested/*"
922+
});
923+
assert.deepEqual(node.db.setValue("test/new/unchartered/nested/$", 12345), {
924+
"code": 7,
925+
"error_message": "Invalid path: /test/new/unchartered/nested/$"
926+
});
927+
assert.deepEqual(node.db.setValue("test/new/unchartered/nested/#", 12345), {
928+
"code": 7,
929+
"error_message": "Invalid path: /test/new/unchartered/nested/#"
930+
});
931+
assert.deepEqual(node.db.setValue("test/new/unchartered/nested/{", 12345), {
932+
"code": 7,
933+
"error_message": "Invalid path: /test/new/unchartered/nested/{"
934+
});
935+
assert.deepEqual(node.db.setValue("test/new/unchartered/nested/}", 12345), {
936+
"code": 7,
937+
"error_message": "Invalid path: /test/new/unchartered/nested/}"
938+
});
939+
assert.deepEqual(node.db.setValue("test/new/unchartered/nested/[", 12345), {
940+
"code": 7,
941+
"error_message": "Invalid path: /test/new/unchartered/nested/["
942+
});
943+
assert.deepEqual(node.db.setValue("test/new/unchartered/nested/]", 12345), {
944+
"code": 7,
945+
"error_message": "Invalid path: /test/new/unchartered/nested/]"
946+
});
947+
assert.deepEqual(node.db.setValue("test/new/unchartered/nested/\x00", 12345), {
948+
"code": 7,
949+
"error_message": "Invalid path: /test/new/unchartered/nested/\x00"
950+
});
951+
assert.deepEqual(node.db.setValue("test/new/unchartered/nested/\x1F", 12345), {
952+
"code": 7,
953+
"error_message": "Invalid path: /test/new/unchartered/nested/\x1F"
954+
});
955+
assert.deepEqual(node.db.setValue("test/new/unchartered/nested/\x7F", 12345), {
956+
"code": 7,
957+
"error_message": "Invalid path: /test/new/unchartered/nested/\x7F"
958+
});
959+
})
913960
})
914961

915962
describe("incValue operations", () => {
@@ -970,6 +1017,13 @@ describe("DB operations", () => {
9701017
});
9711018
expect(node.db.getFunction("test/new2/unchartered/nested/path2")).to.equal(null)
9721019
})
1020+
1021+
it("when writing with invalid path", () => {
1022+
assert.deepEqual(node.db.setRule("/test/test_function/some/path/.", "some function config"), {
1023+
"code": 7,
1024+
"error_message": "Invalid path: /test/test_function/some/path/."
1025+
});
1026+
})
9731027
})
9741028

9751029
describe("setRule operations", () => {
@@ -986,6 +1040,13 @@ describe("DB operations", () => {
9861040
});
9871041
expect(node.db.getRule("/test/test_rule/some/path2")).to.equal(null)
9881042
})
1043+
1044+
it("when writing with invalid path", () => {
1045+
assert.deepEqual(node.db.setRule("/test/test_rule/some/path/.", "some rule config"), {
1046+
"code": 7,
1047+
"error_message": "Invalid path: /test/test_rule/some/path/."
1048+
});
1049+
})
9891050
})
9901051

9911052
describe("setOwner operations", () => {
@@ -1002,6 +1063,13 @@ describe("DB operations", () => {
10021063
});
10031064
expect(node.db.getOwner("/test/test_owner/some/path2")).to.equal(null)
10041065
})
1066+
1067+
it("when writing with invalid path", () => {
1068+
assert.deepEqual(node.db.setRule("/test/test_owner/some/path/.", "some owner config"), {
1069+
"code": 7,
1070+
"error_message": "Invalid path: /test/test_owner/some/path/."
1071+
});
1072+
})
10051073
})
10061074

10071075
describe("set operations", () => {

0 commit comments

Comments
 (0)