Skip to content

Commit

Permalink
Ensure returned versionId is a string when listing objects
Browse files Browse the repository at this point in the history
Returned versionId is a string when calling statObject(), as it is read
without transformation (casting) from the headers.

However, the xml parser casts automatically what looks like a number in
the responses. Consequentially, when the S3 storage provider returns a
VersionId with only numbers, listObjects() returns an object with a
versionId as a number, which is inconsistent with what returns other
methods like statObjects() and may result in erratic behaviors for the
program using these methods.
  • Loading branch information
fflorent committed Jul 20, 2023
1 parent 29665bd commit 4552f57
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
4 changes: 4 additions & 0 deletions src/internal/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,10 @@ export function sanitizeObjectKey(objectName: string): string {
return decodeURIComponent(asStrName)
}

export function sanitizeSize(size?: string): number | undefined {
return size ? Number.parseInt(size) : undefined
}

export const PART_CONSTRAINTS = {
// absMinPartSize - absolute minimum part size (5 MiB)
ABS_MIN_PART_SIZE: 1024 * 1024 * 5,
Expand Down
23 changes: 19 additions & 4 deletions src/xml-parsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@
*/

import crc32 from 'buffer-crc32'
import { XMLParser } from 'fast-xml-parser'

import * as errors from './errors.ts'
import { SelectResults } from './helpers.ts'
import { isObject, parseXml, readableStream, sanitizeETag, sanitizeObjectKey, toArray } from './internal/helper.ts'
import {
isObject,
parseXml,
readableStream,
sanitizeETag,
sanitizeObjectKey,
sanitizeSize,
toArray,
} from './internal/helper.ts'
import { RETENTION_VALIDITY_UNITS } from './internal/type.ts'

// parse XML response for copy object
Expand Down Expand Up @@ -216,12 +225,13 @@ const formatObjInfo = (content, opts = {}) => {
const name = sanitizeObjectKey(toArray(Key)[0])
const lastModified = new Date(toArray(LastModified)[0])
const etag = sanitizeETag(toArray(ETag)[0])
const size = sanitizeSize(Size)

return {
name,
lastModified,
etag,
size: Size,
size,
versionId: VersionId,
isLatest: IsLatest,
isDeleteMarker: opts.IsDeleteMarker ? opts.IsDeleteMarker : false,
Expand All @@ -236,7 +246,12 @@ export function parseListObjects(xml) {
}
let isTruncated = false
let nextMarker, nextVersionKeyMarker
const xmlobj = parseXml(xml)
const fxp = new XMLParser({
numberParseOptions: {
skipLike: /./,
},
})
const xmlobj = fxp.parse(xml)

const parseCommonPrefixesEntity = (responseEntity) => {
if (responseEntity) {
Expand All @@ -258,7 +273,7 @@ export function parseListObjects(xml) {
const name = sanitizeObjectKey(toArray(content.Key)[0])
const lastModified = new Date(toArray(content.LastModified)[0])
const etag = sanitizeETag(toArray(content.ETag)[0])
const size = content.Size
const size = sanitizeSize(content.Size)
result.objects.push({ name, lastModified, etag, size })
})
}
Expand Down
54 changes: 54 additions & 0 deletions tests/unit/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
partsRequired,
} from '../../src/internal/helper.ts'
import * as Minio from '../../src/minio.js'
import { parseListObjects } from '../../src/xml-parsers.ts'

const Package = { version: 'development' }

Expand Down Expand Up @@ -2078,3 +2079,56 @@ describe('IP Address Validations', () => {
})
})
})

describe('xml-parser', () => {
describe('#listObjects()', () => {
describe('value type casting', () => {
const xml = `
<?xml version="1.0" encoding="UTF-8"?>
<ListVersionsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>some-bucket</Name>
<Prefix>42</Prefix>
<Delimiter>/</Delimiter>
<IsTruncated>false</IsTruncated>
<EncodingType>url</EncodingType>
<KeyMarker/>
<VersionIdMarker/>
<Version>
<IsLatest>true</IsLatest>
<VersionId>1234</VersionId>
<ETag>"767dedcb515a0e2d995ed95191b75484-29"</ETag>
<Key>1337</Key>
<LastModified>2023-07-12T14:41:46.000Z</LastModified>
<Size>151306240</Size>
</Version>
<DeleteMarker>
<IsLatest>false</IsLatest>
<Key>1337</Key>
<LastModified>2023-07-12T14:39:22.000Z</LastModified>
<VersionId>5678</VersionId>
</DeleteMarker>
<CommonPrefixes>
<Prefix>42</Prefix>
</CommonPrefixes>
</ListVersionsResult>
`

it('should parse VersionId as string even if number is provided', () => {
const { objects } = parseListObjects(xml)

assert.equal(objects[0].versionId, '1234')
assert.equal(objects[1].versionId, '5678')
assert.equal(objects[0].name, '1337')
assert.equal(objects[1].name, '1337')
assert.deepEqual(objects[2], { prefix: '42', size: 0 })
})

it('should parse Size as number', () => {
const { objects } = parseListObjects(xml)

assert.equal(objects[0].size, 151306240)
assert.equal(objects[1].size, undefined)
})
})
})
})

0 comments on commit 4552f57

Please sign in to comment.