Skip to content

Commit

Permalink
Merge commit from fork
Browse files Browse the repository at this point in the history
* fix: prototype pollution in `handleFlatJson`

* chore: tweak for release

* chore: add permissions for github actions
  • Loading branch information
kazupon authored Mar 7, 2025
1 parent 4e55512 commit fbda998
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 50 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:
- 'v*'
env:
NODE_OPTIONS: --max-old-space-size=6144
permissions:
id-token: write
contents: write

jobs:
release:
Expand Down Expand Up @@ -59,3 +62,4 @@ jobs:
./scripts/release.sh
env:
NPM_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
NPM_CONFIG_PROVENANCE: true
3 changes: 3 additions & 0 deletions packages/vue-i18n-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ export function handleFlatJson(obj: unknown): unknown {
let currentObj = obj
let hasStringValue = false
for (let i = 0; i < lastIndex; i++) {
if (subKeys[i] === '__proto__') {
throw new Error(`unsafe key: ${subKeys[i]}`)
}
if (!(subKeys[i] in currentObj)) {
currentObj[subKeys[i]] = create()
}
Expand Down
113 changes: 64 additions & 49 deletions packages/vue-i18n-core/test/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,80 @@
// utils
import * as shared from '@intlify/shared'
import { handleFlatJson } from '../src/utils'
import { I18nWarnCodes, getWarnMessage } from '../src/warnings'
vi.mock('@intlify/shared', async () => {
const actual = await vi.importActual<object>('@intlify/shared')
return {
...actual,
warn: vi.fn()
}
})
import { handleFlatJson } from '../src/utils'
import { I18nWarnCodes, getWarnMessage } from '../src/warnings'

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

const obj = {
a: { a1: 'a1.value' },
'a.a2': 'a.a2.value',
'b.x': {
'b1.x': 'b1.x.value',
'b2.x': ['b2.x.value0', 'b2.x.value1'],
'b3.x': { 'b3.x': 'b3.x.value' }
},
c: {
'animal.dog': 'Dog',
animal: 'Animal'
},
d: {
'animal.dog': 'Dog',
animal: {}
}
}
const expectObj = {
a: {
a1: 'a1.value',
a2: 'a.a2.value'
},
b: {
x: {
b1: { x: 'b1.x.value' },
b2: { x: ['b2.x.value0', 'b2.x.value1'] },
b3: { x: { b3: { x: 'b3.x.value' } } }
const obj = {
a: { a1: 'a1.value' },
'a.a2': 'a.a2.value',
'b.x': {
'b1.x': 'b1.x.value',
'b2.x': ['b2.x.value0', 'b2.x.value1'],
'b3.x': { 'b3.x': 'b3.x.value' }
},
c: {
'animal.dog': 'Dog',
animal: 'Animal'
},
d: {
'animal.dog': 'Dog',
animal: {}
}
},
c: {
'animal.dog': 'Dog',
animal: 'Animal'
},
d: {
animal: {
dog: 'Dog'
}
const expectObj = {
a: {
a1: 'a1.value',
a2: 'a.a2.value'
},
b: {
x: {
b1: { x: 'b1.x.value' },
b2: { x: ['b2.x.value0', 'b2.x.value1'] },
b3: { x: { b3: { x: 'b3.x.value' } } }
}
},
c: {
'animal.dog': 'Dog',
animal: 'Animal'
},
d: {
animal: {
dog: 'Dog'
}
}
}
}

expect(handleFlatJson(obj)).toEqual(expectObj)
expect(mockWarn).toHaveBeenCalled()
expect(mockWarn.mock.calls[0][0]).toEqual(
getWarnMessage(I18nWarnCodes.IGNORE_OBJ_FLATTEN, {
key: 'animal'
})
)
expect(handleFlatJson(obj)).toEqual(expectObj)
expect(mockWarn).toHaveBeenCalled()
expect(mockWarn.mock.calls[0][0]).toEqual(
getWarnMessage(I18nWarnCodes.IGNORE_OBJ_FLATTEN, {
key: 'animal'
})
)
})

// security advisories
// ref: https://github.com/intlify/vue-i18n/security/advisories/GHSA-p2ph-7g93-hw3m
test('prototype pollution', () => {
expect(() =>
handleFlatJson({ '__proto__.pollutedKey': 'pollutedValue' })
).toThrow()
// @ts-ignore -- test
// eslint-disable-next-line no-proto
expect({}.__proto__.pollutedKey).toBeUndefined()
// @ts-ignore -- test
expect(Object.prototype.pollutedKey).toBeUndefined()
})
})
2 changes: 1 addition & 1 deletion scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ for PKG in packages/* ; do
continue
fi
pushd $PKG
TAG="latest"
TAG="legacy10"
echo "⚡ Publishing $PKG with tag $TAG"
pnpm publish --access public --no-git-checks --tag $TAG
popd > /dev/null
Expand Down

0 comments on commit fbda998

Please sign in to comment.