Skip to content

Commit fbda998

Browse files
authored
Merge commit from fork
* fix: prototype pollution in `handleFlatJson` * chore: tweak for release * chore: add permissions for github actions
1 parent 4e55512 commit fbda998

File tree

4 files changed

+72
-50
lines changed

4 files changed

+72
-50
lines changed

.github/workflows/release.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ on:
88
- 'v*'
99
env:
1010
NODE_OPTIONS: --max-old-space-size=6144
11+
permissions:
12+
id-token: write
13+
contents: write
1114

1215
jobs:
1316
release:
@@ -59,3 +62,4 @@ jobs:
5962
./scripts/release.sh
6063
env:
6164
NPM_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
65+
NPM_CONFIG_PROVENANCE: true

packages/vue-i18n-core/src/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ export function handleFlatJson(obj: unknown): unknown {
6969
let currentObj = obj
7070
let hasStringValue = false
7171
for (let i = 0; i < lastIndex; i++) {
72+
if (subKeys[i] === '__proto__') {
73+
throw new Error(`unsafe key: ${subKeys[i]}`)
74+
}
7275
if (!(subKeys[i] in currentObj)) {
7376
currentObj[subKeys[i]] = create()
7477
}
Lines changed: 64 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,80 @@
11
// utils
22
import * as shared from '@intlify/shared'
3+
import { handleFlatJson } from '../src/utils'
4+
import { I18nWarnCodes, getWarnMessage } from '../src/warnings'
35
vi.mock('@intlify/shared', async () => {
46
const actual = await vi.importActual<object>('@intlify/shared')
57
return {
68
...actual,
79
warn: vi.fn()
810
}
911
})
10-
import { handleFlatJson } from '../src/utils'
11-
import { I18nWarnCodes, getWarnMessage } from '../src/warnings'
1212

13-
test('handleFlatJson', () => {
14-
const mockWarn = vi.spyOn(shared, 'warn')
15-
// eslint-disable-next-line @typescript-eslint/no-empty-function
16-
mockWarn.mockImplementation(() => {})
13+
describe('handleFlatJson', () => {
14+
test('basic', () => {
15+
const mockWarn = vi.spyOn(shared, 'warn')
16+
// eslint-disable-next-line @typescript-eslint/no-empty-function
17+
mockWarn.mockImplementation(() => {})
1718

18-
const obj = {
19-
a: { a1: 'a1.value' },
20-
'a.a2': 'a.a2.value',
21-
'b.x': {
22-
'b1.x': 'b1.x.value',
23-
'b2.x': ['b2.x.value0', 'b2.x.value1'],
24-
'b3.x': { 'b3.x': 'b3.x.value' }
25-
},
26-
c: {
27-
'animal.dog': 'Dog',
28-
animal: 'Animal'
29-
},
30-
d: {
31-
'animal.dog': 'Dog',
32-
animal: {}
33-
}
34-
}
35-
const expectObj = {
36-
a: {
37-
a1: 'a1.value',
38-
a2: 'a.a2.value'
39-
},
40-
b: {
41-
x: {
42-
b1: { x: 'b1.x.value' },
43-
b2: { x: ['b2.x.value0', 'b2.x.value1'] },
44-
b3: { x: { b3: { x: 'b3.x.value' } } }
19+
const obj = {
20+
a: { a1: 'a1.value' },
21+
'a.a2': 'a.a2.value',
22+
'b.x': {
23+
'b1.x': 'b1.x.value',
24+
'b2.x': ['b2.x.value0', 'b2.x.value1'],
25+
'b3.x': { 'b3.x': 'b3.x.value' }
26+
},
27+
c: {
28+
'animal.dog': 'Dog',
29+
animal: 'Animal'
30+
},
31+
d: {
32+
'animal.dog': 'Dog',
33+
animal: {}
4534
}
46-
},
47-
c: {
48-
'animal.dog': 'Dog',
49-
animal: 'Animal'
50-
},
51-
d: {
52-
animal: {
53-
dog: 'Dog'
35+
}
36+
const expectObj = {
37+
a: {
38+
a1: 'a1.value',
39+
a2: 'a.a2.value'
40+
},
41+
b: {
42+
x: {
43+
b1: { x: 'b1.x.value' },
44+
b2: { x: ['b2.x.value0', 'b2.x.value1'] },
45+
b3: { x: { b3: { x: 'b3.x.value' } } }
46+
}
47+
},
48+
c: {
49+
'animal.dog': 'Dog',
50+
animal: 'Animal'
51+
},
52+
d: {
53+
animal: {
54+
dog: 'Dog'
55+
}
5456
}
5557
}
56-
}
5758

58-
expect(handleFlatJson(obj)).toEqual(expectObj)
59-
expect(mockWarn).toHaveBeenCalled()
60-
expect(mockWarn.mock.calls[0][0]).toEqual(
61-
getWarnMessage(I18nWarnCodes.IGNORE_OBJ_FLATTEN, {
62-
key: 'animal'
63-
})
64-
)
59+
expect(handleFlatJson(obj)).toEqual(expectObj)
60+
expect(mockWarn).toHaveBeenCalled()
61+
expect(mockWarn.mock.calls[0][0]).toEqual(
62+
getWarnMessage(I18nWarnCodes.IGNORE_OBJ_FLATTEN, {
63+
key: 'animal'
64+
})
65+
)
66+
})
67+
68+
// security advisories
69+
// ref: https://github.com/intlify/vue-i18n/security/advisories/GHSA-p2ph-7g93-hw3m
70+
test('prototype pollution', () => {
71+
expect(() =>
72+
handleFlatJson({ '__proto__.pollutedKey': 'pollutedValue' })
73+
).toThrow()
74+
// @ts-ignore -- test
75+
// eslint-disable-next-line no-proto
76+
expect({}.__proto__.pollutedKey).toBeUndefined()
77+
// @ts-ignore -- test
78+
expect(Object.prototype.pollutedKey).toBeUndefined()
79+
})
6580
})

scripts/release.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ for PKG in packages/* ; do
2323
continue
2424
fi
2525
pushd $PKG
26-
TAG="latest"
26+
TAG="legacy10"
2727
echo "⚡ Publishing $PKG with tag $TAG"
2828
pnpm publish --access public --no-git-checks --tag $TAG
2929
popd > /dev/null

0 commit comments

Comments
 (0)