Skip to content

Commit 99af3fc

Browse files
authored
Merge commit from fork
* fix(architecture): handle arbitrary prototype pollution Update the `ArchitectureGroupAlignments` to use a `Map<`${string}-${string},` instead of being a `Record<string, Record<string,`. Not only does this make the code simpler, but it also means we avoid arbritary prototype pollution attacks. Fixes: GHSA-3rrr-jr9j-h3q3 Fixes: cb0a470 * refactor(architecture): use `Map` for registeredIds * fix(architecture): improve group ID handling `__proto__`, `constructor`, `toString`, etc. can now be used as a group ID in architecture diagrams. * refactor(architecture): use Map for nodes in DB * refactor(architecture): use Map for spatial maps * refactor(architecture): use `Set` for visit record * refactor(architecture): use Map for `adjList` * refactor(architecture): use Map for elements storage * refactor(architecture): use Map for alignments * test(architecture): test for service ID sorting The old code sorted services by their ID using object iteration order, which would sort numeric keys before string keys. Now they are sorted in insertion order, so you can change around the order of `service` statements in your diagram. * test(architecture): test for GHSA-3rrr-jr9j-h3q3 Add a modified test to confirm that the prototype pollution in GHSA-3rrr-jr9j-h3q3 is no longer exploitable.
1 parent 2337f7e commit 99af3fc

6 files changed

Lines changed: 232 additions & 168 deletions

File tree

‎.changeset/dry-ducks-agree.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'mermaid': patch
3+
---
4+
5+
fix(architecture): use `Map`s and `Set`s to store groups/services
6+
7+
Services are now rendered in the order they are defined and more service IDs
8+
are now supported.
9+
10+
author: aloisklink

‎.cspell/misc-terms.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ BRANDES
22
browserlist
33
Buzan
44
circo
5+
GHSA
56
handDrawn
67
KOEPF
78
MMLU

‎packages/mermaid/src/diagrams/architecture/architecture.spec.ts‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,51 @@ describe('architecture diagrams', () => {
195195
});
196196
});
197197

198+
it('should throw when missing a group', async () => {
199+
const str = `architecture-beta
200+
group mermaidPrototypePollutionMarker(cloud)[Marker]
201+
service a(server)[A] in __proto__
202+
service b(server)[B] in mermaidPrototypePollutionMarker
203+
a:R -- L:b`;
204+
// __proto__ group does not exist.
205+
await expect(parser.parse(str)).rejects.toThrow(/The service \[a]'s parent does not exist./);
206+
});
207+
208+
it('should allow __proto__ as a group name', async () => {
209+
const str = `architecture-beta
210+
group __proto__(cloud)[Marker]
211+
service a(server)[A] in __proto__
212+
service b(server)[B] in __proto__
213+
a:R -- L:b`;
214+
await expect(parser.parse(str)).resolves.not.toThrow();
215+
expect(db.getGroups().map((g) => g.id)).toContain('__proto__');
216+
});
217+
218+
it('should block proto pollution via service id', async () => {
219+
const str = `architecture-beta
220+
group __proto__(cloud)[P]
221+
group myPrototypePollutionKey(cloud)[Real]
222+
service a(server)[A] in __proto__
223+
service b(server)[B] in myPrototypePollutionKey
224+
a:R -- L:b`;
225+
226+
await expect(parser.parse(str)).resolves.not.toThrow();
227+
// GHSA-3rrr-jr9j-h3q3 happened in this function
228+
const structures = db.getDataStructures();
229+
expect(structures.groupAlignments.get(`"__proto__"-"myPrototypePollutionKey"`)).toBeDefined();
230+
expect(Object.prototype).not.toHaveProperty('myPrototypePollutionKey');
231+
});
232+
233+
it('should store services in the order they were added', async () => {
234+
const str = `architecture-beta
235+
service a(server)[A]
236+
service 20(server)[B]
237+
service 10(server)[C]`;
238+
await expect(parser.parse(str)).resolves.not.toThrow();
239+
// Old object iteration order rules sorts numeric keys first
240+
expect(db.getServices().map((s) => s.id)).toEqual(['a', '20', '10']);
241+
});
242+
198243
describe('align directive', () => {
199244
it('should parse a row alignment and expose it via getLayoutHints', async () => {
200245
const str = `architecture-beta

0 commit comments

Comments
 (0)