Skip to content

Commit a00b29c

Browse files
author
Chad Wagner
committed
Prevent collisions from silently mapping to same alias
- instead of using char+ -> _ - use char -> `_x${char.charCodeAt(0).toString(16)}_` (basically char code surrouned by underscores)
1 parent 279eee5 commit a00b29c

2 files changed

Lines changed: 21 additions & 11 deletions

File tree

src/sinks/dynamodb.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export const updateExpression = (Item) => {
1313
// If this attribute ends with '_delete'...assume we're deleting values from a set.
1414
const isDeleteSet = key.endsWith('_delete');
1515
const baseKey = isDeleteSet ? key.replace(/_delete$/, '') : key;
16-
const alias = baseKey.replace(/([^a-z0-9_]+)/gi, '_');
16+
const alias = baseKey.replace(/([^a-z0-9_])/gi, (char) =>
17+
`_x${char.charCodeAt(0).toString(16)}_`);
1718

1819
acc.ExpressionAttributeNames[`#${alias}`] = baseKey;
1920

test/unit/sinks/dynamodb.test.js

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,8 @@ describe('sinks/dynamodb.js', () => {
3939
'#id': 'id',
4040
'#latched': 'latched',
4141
'#name': 'name',
42-
'#some_unsafe_att_name': 'some unsafe att name',
43-
'#some_unsafe_att_name_to_delete': 'some unsafe att name to delete',
44-
// '#status': 'status',
42+
'#some_x20_unsafe_x20_att_x20_name': 'some unsafe att name',
43+
'#some_x20_unsafe_x20_att_x20_name_x20_to_x20_delete': 'some unsafe att name to delete',
4544
'#status2': 'status2',
4645
'#timestamp': 'timestamp',
4746
'#ttl': 'ttl',
@@ -52,13 +51,11 @@ describe('sinks/dynamodb.js', () => {
5251
':id': '2f8ac025-d9e3-48f9-ba80-56487ddf0b89',
5352
':latched': true,
5453
':name': 'Thing One',
55-
':some_unsafe_att_name': true,
56-
// ':status': undefined,
57-
// ':status2': null,
54+
':some_x20_unsafe_x20_att_x20_name': true,
5855
':timestamp': 1540454400000,
5956
':ttl': 1543046400,
6057
},
61-
UpdateExpression: 'SET #id = :id, #name = :name, #description = :description, #discriminator = :discriminator, #latched = :latched, #ttl = :ttl, #timestamp = :timestamp, #some_unsafe_att_name = :some_unsafe_att_name REMOVE #status2, #some_unsafe_att_name_to_delete',
58+
UpdateExpression: 'SET #id = :id, #name = :name, #description = :description, #discriminator = :discriminator, #latched = :latched, #ttl = :ttl, #timestamp = :timestamp, #some_x20_unsafe_x20_att_x20_name = :some_x20_unsafe_x20_att_x20_name REMOVE #status2, #some_x20_unsafe_x20_att_x20_name_x20_to_x20_delete',
6259
ReturnValues: 'ALL_NEW',
6360
});
6461
});
@@ -100,16 +97,28 @@ describe('sinks/dynamodb.js', () => {
10097
it('should calculate updateExpression removing values from a set when attribute names have illegal characters if used as an alias', () => {
10198
const result = updateExpression({
10299
'some|tags_delete': new Set(['x', 'y']),
100+
'a-b': true,
101+
'a--b': false,
102+
'a|b': 1,
103103
});
104104

105105
expect(normalizeObj(result)).to.deep.equal({
106106
ExpressionAttributeNames: {
107-
'#some_tags': 'some|tags',
107+
'#some_x7c_tags': 'some|tags',
108+
'#a_x2d_b': 'a-b',
109+
'#a_x2d__x2d_b': 'a--b',
110+
'#a_x7c_b': 'a|b',
108111
},
109112
ExpressionAttributeValues: {
110-
':some_tags_delete': ['x', 'y'],
113+
':some_x7c_tags_delete': [
114+
'x',
115+
'y',
116+
],
117+
':a_x2d_b': true,
118+
':a_x2d__x2d_b': false,
119+
':a_x7c_b': 1,
111120
},
112-
UpdateExpression: 'DELETE #some_tags :some_tags_delete',
121+
UpdateExpression: 'SET #a_x2d_b = :a_x2d_b, #a_x2d__x2d_b = :a_x2d__x2d_b, #a_x7c_b = :a_x7c_b DELETE #some_x7c_tags :some_x7c_tags_delete',
113122
ReturnValues: 'ALL_NEW',
114123
});
115124
});

0 commit comments

Comments
 (0)