Skip to content

Commit 4f53378

Browse files
authored
Ensure returned versionId is a string when listing objects (#1193)
1 parent 83fb55e commit 4f53378

File tree

3 files changed

+78
-4
lines changed

3 files changed

+78
-4
lines changed

src/internal/helper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,10 @@ export function sanitizeObjectKey(objectName: string): string {
462462
return decodeURIComponent(asStrName)
463463
}
464464

465+
export function sanitizeSize(size?: string): number | undefined {
466+
return size ? Number.parseInt(size) : undefined
467+
}
468+
465469
export const PART_CONSTRAINTS = {
466470
// absMinPartSize - absolute minimum part size (5 MiB)
467471
ABS_MIN_PART_SIZE: 1024 * 1024 * 5,

src/xml-parsers.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,27 @@
1515
*/
1616

1717
import crc32 from 'buffer-crc32'
18+
import { XMLParser } from 'fast-xml-parser'
1819

1920
import * as errors from './errors.ts'
2021
import { SelectResults } from './helpers.ts'
21-
import { isObject, parseXml, readableStream, sanitizeETag, sanitizeObjectKey, toArray } from './internal/helper.ts'
22+
import {
23+
isObject,
24+
parseXml,
25+
readableStream,
26+
sanitizeETag,
27+
sanitizeObjectKey,
28+
sanitizeSize,
29+
toArray,
30+
} from './internal/helper.ts'
2231
import { RETENTION_VALIDITY_UNITS } from './internal/type.ts'
2332

33+
const fxpWithoutNumParser = new XMLParser({
34+
numberParseOptions: {
35+
skipLike: /./,
36+
},
37+
})
38+
2439
// parse XML response for copy object
2540
export function parseCopyObject(xml) {
2641
var result = {
@@ -201,12 +216,13 @@ const formatObjInfo = (content, opts = {}) => {
201216
const name = sanitizeObjectKey(toArray(Key)[0])
202217
const lastModified = new Date(toArray(LastModified)[0])
203218
const etag = sanitizeETag(toArray(ETag)[0])
219+
const size = sanitizeSize(Size)
204220

205221
return {
206222
name,
207223
lastModified,
208224
etag,
209-
size: Size,
225+
size,
210226
versionId: VersionId,
211227
isLatest: IsLatest,
212228
isDeleteMarker: opts.IsDeleteMarker ? opts.IsDeleteMarker : false,
@@ -221,7 +237,7 @@ export function parseListObjects(xml) {
221237
}
222238
let isTruncated = false
223239
let nextMarker, nextVersionKeyMarker
224-
const xmlobj = parseXml(xml)
240+
const xmlobj = fxpWithoutNumParser.parse(xml)
225241

226242
const parseCommonPrefixesEntity = (responseEntity) => {
227243
if (responseEntity) {
@@ -243,7 +259,7 @@ export function parseListObjects(xml) {
243259
const name = sanitizeObjectKey(toArray(content.Key)[0])
244260
const lastModified = new Date(toArray(content.LastModified)[0])
245261
const etag = sanitizeETag(toArray(content.ETag)[0])
246-
const size = content.Size
262+
const size = sanitizeSize(content.Size)
247263
result.objects.push({ name, lastModified, etag, size })
248264
})
249265
}

tests/unit/test.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
partsRequired,
3030
} from '../../src/internal/helper.ts'
3131
import * as Minio from '../../src/minio.js'
32+
import { parseListObjects } from '../../src/xml-parsers.js'
3233

3334
const Package = { version: 'development' }
3435

@@ -2123,3 +2124,56 @@ describe('IP Address Validations', () => {
21232124
})
21242125
})
21252126
})
2127+
2128+
describe('xml-parser', () => {
2129+
describe('#listObjects()', () => {
2130+
describe('value type casting', () => {
2131+
const xml = `
2132+
<?xml version="1.0" encoding="UTF-8"?>
2133+
<ListVersionsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
2134+
<Name>some-bucket</Name>
2135+
<Prefix>42</Prefix>
2136+
<Delimiter>/</Delimiter>
2137+
<IsTruncated>false</IsTruncated>
2138+
<EncodingType>url</EncodingType>
2139+
<KeyMarker/>
2140+
<VersionIdMarker/>
2141+
<Version>
2142+
<IsLatest>true</IsLatest>
2143+
<VersionId>1234</VersionId>
2144+
<ETag>"767dedcb515a0e2d995ed95191b75484-29"</ETag>
2145+
<Key>1337</Key>
2146+
<LastModified>2023-07-12T14:41:46.000Z</LastModified>
2147+
<Size>151306240</Size>
2148+
</Version>
2149+
<DeleteMarker>
2150+
<IsLatest>false</IsLatest>
2151+
<Key>1337</Key>
2152+
<LastModified>2023-07-12T14:39:22.000Z</LastModified>
2153+
<VersionId>5678</VersionId>
2154+
</DeleteMarker>
2155+
<CommonPrefixes>
2156+
<Prefix>42</Prefix>
2157+
</CommonPrefixes>
2158+
</ListVersionsResult>
2159+
`
2160+
2161+
it('should parse VersionId as string even if number is provided', () => {
2162+
const { objects } = parseListObjects(xml)
2163+
2164+
assert.equal(objects[0].versionId, '1234')
2165+
assert.equal(objects[1].versionId, '5678')
2166+
assert.equal(objects[0].name, '1337')
2167+
assert.equal(objects[1].name, '1337')
2168+
assert.deepEqual(objects[2], { prefix: '42', size: 0 })
2169+
})
2170+
2171+
it('should parse Size as number', () => {
2172+
const { objects } = parseListObjects(xml)
2173+
2174+
assert.equal(objects[0].size, 151306240)
2175+
assert.equal(objects[1].size, undefined)
2176+
})
2177+
})
2178+
})
2179+
})

0 commit comments

Comments
 (0)