diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 53c1c80732..ddfb379180 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -26,42 +26,33 @@ docker pull ghcr.io/scality/cloudserver: To release a production image: -* Create a PR to bump the package version - Update Cloudserver's `package.json` by bumping it to the relevant next - version in a new PR. Per example if the last released version was - `8.4.7`, the next version would be `8.4.8`. - -```js -{ - "name": "cloudserver", - "version": "8.4.8", <--- Here - [...] -} -``` +* Create a PR to bump the package version : + update Cloudserver's `package.json` by bumping it to the relevant next + version in a new PR. Per example if the last released version was `8.4.7`, + the next version would be `8.4.8`. + + ```js + { + "name": "cloudserver", + "version": "8.4.8", <--- Here + [...] + } + ``` * Review & merge the PR -* Create the release on GitHub - - * Go the Release tab (https://github.com/scality/cloudserver/releases); - * Click on the `Draft new release button`; - * In the `tag` field, type the name of the release (`8.4.8`), and confirm - to create the tag on publish; - * Click on `Generate release notes` button to fill the fields; - * Rename the release to `Release x.y.z` (e.g. `Release 8.4.8` in this case); - * Click to `Publish the release` to create the GitHub release and git tag - - Notes: - * the Git tag will be created automatically. - * this should be done as soon as the PR is merged, so that the tag - is put on the "version bump" commit. - -* With the following parameters, [force a build here](https://eve.devsca.com/github/scality/cloudserver/#/builders/3/force/force) - - * Branch Name: The one used for the tag earlier. In this example `development/8.4` - * Override Stage: 'release' - * Extra properties: - * name: `'tag'`, value: `[release version]`, in this example`'8.4.8'` +* Trigger the release workflow on GitHub + + * Go to the [**Actions** tab on GitHub](https://github.com/scality/cloudserver/actions) + * Select the `release` workflow from the list + * Click on **Run workflow** (manual dispatch) + * Enter the new tag (e.g., `8.4.8`) in the input field + * Start the workflow + + This workflow will create the tag and push the Docker images. + + This should be done as soon as the PR is merged, + so that the tag is put on the "version bump" commit. * Release the release version on Jira diff --git a/lib/api/apiUtils/authorization/prepareRequestContexts.js b/lib/api/apiUtils/authorization/prepareRequestContexts.js index c33fcc9c82..c62ace060a 100644 --- a/lib/api/apiUtils/authorization/prepareRequestContexts.js +++ b/lib/api/apiUtils/authorization/prepareRequestContexts.js @@ -252,6 +252,15 @@ function prepareRequestContexts(apiMethod, request, sourceBucket, generateRequestContext('bypassGovernanceRetention'); requestContexts.push(checkUserGovernanceBypassRequestContext); } + } else if (apiMethodAfterVersionCheck === 'bucketGet') { + requestContexts.push(generateRequestContext(apiMethodAfterVersionCheck)); + + const optionalAttributesHeader = request.headers['x-amz-optional-object-attributes']; + const requestedAttributes = optionalAttributesHeader ? optionalAttributesHeader.split(',') : []; + + if (requestedAttributes.filter(attr => attr != 'RestoreStatus').length > 0) { + requestContexts.push(generateRequestContext('listObjectsV2OptionalAttributes')); + } } else { const requestContext = generateRequestContext(apiMethodAfterVersionCheck); diff --git a/lib/api/bucketGet.js b/lib/api/bucketGet.js index 8eef7694e4..64b5322fdd 100644 --- a/lib/api/bucketGet.js +++ b/lib/api/bucketGet.js @@ -284,6 +284,15 @@ function bucketGet(authInfo, request, log, callback) { const params = request.query; const bucketName = request.bucketName; const v2 = params['list-type']; + + const optionalAttributes = + request.headers['x-amz-optional-object-attributes']?.split(',').map(attr => attr.trim()) ?? []; + if (optionalAttributes.some(attr => !attr.startsWith('x-amz-meta-') && attr != 'RestoreStatus')) { + return callback( + errorInstances.InvalidArgument.customizeDescription('Invalid attribute name specified') + ); + } + if (v2 !== undefined && Number.parseInt(v2, 10) !== 2) { return callback(errorInstances.InvalidArgument.customizeDescription('Invalid ' + 'List Type specified in Request')); @@ -344,14 +353,14 @@ function bucketGet(authInfo, request, log, callback) { } standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => { - const corsHeaders = collectCorsHeaders(request.headers.origin, - request.method, bucket); + const corsHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket); + if (err) { log.debug('error processing request', { error: err }); - monitoring.promMetrics( - 'GET', bucketName, err.code, 'listBucket'); + monitoring.promMetrics('GET', bucketName, err.code, 'listBucket'); return callback(err, null, corsHeaders); } + if (params.versions !== undefined) { listParams.listingType = 'DelimiterVersions'; delete listParams.marker; @@ -359,6 +368,7 @@ function bucketGet(authInfo, request, log, callback) { listParams.versionIdMarker = params['version-id-marker'] ? versionIdUtils.decode(params['version-id-marker']) : undefined; } + if (!requestMaxKeys) { const emptyList = { CommonPrefixes: [], @@ -369,14 +379,14 @@ function bucketGet(authInfo, request, log, callback) { return handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, emptyList, corsHeaders, log, callback); } - return services.getObjectListing(bucketName, listParams, log, - (err, list) => { + + return services.getObjectListing(bucketName, listParams, log, (err, list) => { if (err) { log.debug('error processing request', { error: err }); - monitoring.promMetrics( - 'GET', bucketName, err.code, 'listBucket'); + monitoring.promMetrics('GET', bucketName, err.code, 'listBucket'); return callback(err, null, corsHeaders); } + return handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, list, corsHeaders, log, callback); }); diff --git a/package.json b/package.json index a228a88474..df84ef014b 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "dependencies": { "@azure/storage-blob": "^12.28.0", "@hapi/joi": "^17.1.1", - "arsenal": "git+https://github.com/scality/Arsenal#8.2.39", + "arsenal": "git+https://github.com/scality/arsenal#8.2.44", "async": "2.6.4", "aws-sdk": "^2.1692.0", "bucketclient": "scality/bucketclient#8.2.7", @@ -69,6 +69,7 @@ "node-mocks-http": "^1.16.1", "nodemon": "^3.1.10", "nyc": "^15.1.0", + "pino-pretty": "^13.1.3", "sinon": "^13.0.1", "tv4": "^1.3.0" }, @@ -82,7 +83,7 @@ }, "scripts": { "cloudserver": "S3METADATA=mongodb npm-run-all --parallel start_dataserver start_s3server", - "dev": "nodemon --exec \"yarn run start\"", + "dev": "nodemon --exec \"yarn run start\" | pino-pretty -c -S -m message -i \"pid,hostname,name,authn,authz,address,clientPort,clientIP,elapsed_ms\"", "ft_awssdk": "cd tests/functional/aws-node-sdk && mocha --reporter mocha-multi-reporters --reporter-options configFile=$INIT_CWD/tests/reporter-config.json test/ --exit", "ft_awssdk_aws": "cd tests/functional/aws-node-sdk && AWS_ON_AIR=true mocha --reporter mocha-multi-reporters --reporter-options configFile=$INIT_CWD/tests/reporter-config.json test/ --exit", "ft_awssdk_buckets": "cd tests/functional/aws-node-sdk && mocha --reporter mocha-multi-reporters --reporter-options configFile=$INIT_CWD/tests/reporter-config.json test/bucket --exit", diff --git a/tests/functional/aws-node-sdk/test/object/mpuVersion.js b/tests/functional/aws-node-sdk/test/object/mpuVersion.js index 0a89eb7e32..012c3d6e9a 100644 --- a/tests/functional/aws-node-sdk/test/object/mpuVersion.js +++ b/tests/functional/aws-node-sdk/test/object/mpuVersion.js @@ -101,7 +101,7 @@ function checkObjMdAndUpdate(objMDBefore, objMDAfter, props) { }); } -function clearUploadIdFromVersions(versions) { +function clearUploadIdAndRestoreStatusFromVersions(versions) { if (!versions || versions.length === 0) { return versions; } @@ -109,6 +109,7 @@ function clearUploadIdFromVersions(versions) { for (const version of versions) { if (version.value) { version.value.uploadId = undefined; + version.value.restoreStatus = undefined; } } @@ -263,7 +264,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -272,7 +273,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -304,7 +305,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -313,7 +314,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -353,7 +354,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -366,7 +367,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -405,7 +406,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -418,7 +419,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -458,7 +459,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, 'null', next), @@ -467,7 +468,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -511,7 +512,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); next(err); }), next => putMPUVersion(s3, bucketName, objectName, vId, next), @@ -520,7 +521,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -567,7 +568,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -576,7 +577,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -621,7 +622,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, vId, next), @@ -630,7 +631,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -670,7 +671,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -683,7 +684,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -729,7 +730,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -743,7 +744,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { @@ -777,7 +778,7 @@ describe('MPU with x-scal-s3-version-id header', () => { next => s3.putObject(params, next), next => fakeMetadataArchive(bucketName, objectName, undefined, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, undefined, (err, objMD) => { @@ -791,7 +792,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdAndRestoreStatusFromVersions(res.Versions); return next(err); }), ], err => { diff --git a/tests/functional/aws-node-sdk/test/object/putVersion.js b/tests/functional/aws-node-sdk/test/object/putVersion.js index f90d712536..74dc164ff9 100644 --- a/tests/functional/aws-node-sdk/test/object/putVersion.js +++ b/tests/functional/aws-node-sdk/test/object/putVersion.js @@ -34,6 +34,21 @@ function putObjectVersion(s3, params, vid, next) { return request.send(next); } +function clearRestoreStatus(versions) { + if (!versions || versions.length === 0) { + return versions; + } + + for (const version of versions) { + if (version.value) { + version.value.restoreStatus = undefined; + } + } + + return versions; +} + + function checkVersionsAndUpdate(versionsBefore, versionsAfter, indexes) { indexes.forEach(i => { assert.notStrictEqual(versionsAfter[i].value.Size, versionsBefore[i].value.Size); @@ -203,7 +218,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => putObjectVersion(s3, params, '', next), @@ -212,7 +227,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -250,7 +265,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -263,7 +278,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -301,7 +316,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -314,7 +329,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -353,7 +368,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); next(err); }), next => putObjectVersion(s3, params, 'null', next), @@ -362,7 +377,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -405,7 +420,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => putObjectVersion(s3, params, vId, next), @@ -414,7 +429,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -460,7 +475,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => putObjectVersion(s3, params, '', next), @@ -469,7 +484,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -509,7 +524,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => s3.putObject(params, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -522,7 +537,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -561,7 +576,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -574,7 +589,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -619,7 +634,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -633,7 +648,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -666,7 +681,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { next => s3.putObject(params, next), next => fakeMetadataArchive(bucketName, objectName, undefined, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, undefined, (err, objMD) => { @@ -680,7 +695,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { diff --git a/tests/unit/api/apiUtils/authorization/prepareRequestContexts.js b/tests/unit/api/apiUtils/authorization/prepareRequestContexts.js index 6cf2e3c591..0a08b104f3 100644 --- a/tests/unit/api/apiUtils/authorization/prepareRequestContexts.js +++ b/tests/unit/api/apiUtils/authorization/prepareRequestContexts.js @@ -348,4 +348,52 @@ describe('prepareRequestContexts', () => { assert.strictEqual(results[0].getAction(), expectedAction); }); }); + + describe('bucketGet', () => { + describe('x-amz-optional-object-attributes header', () => { + it('should request for specific permission if the header is set', () => { + const apiMethod = 'bucketGet'; + const request = makeRequest({ + 'x-amz-optional-object-attributes': 'x-amz-meta-department', + }); + const results = prepareRequestContexts(apiMethod, request, sourceBucket, sourceObject, sourceVersionId); + + assert.strictEqual(results.length, 2); + assert.strictEqual(results[0].getAction(), 's3:ListBucket'); + assert.strictEqual(results[1].getAction(), 'scality:ListBucketOptionalObjectAttributes'); + }); + + it('should request for specific permission if the header is set with multiple value', () => { + const apiMethod = 'bucketGet'; + const request = makeRequest({ + 'x-amz-optional-object-attributes': 'x-amz-meta-department,RestoreStatus', + }); + const results = prepareRequestContexts(apiMethod, request, sourceBucket, sourceObject, sourceVersionId); + + assert.strictEqual(results.length, 2); + assert.strictEqual(results[0].getAction(), 's3:ListBucket'); + assert.strictEqual(results[1].getAction(), 'scality:ListBucketOptionalObjectAttributes'); + }); + + it('should not request permission if the header contains only RestoreStatus', () => { + const apiMethod = 'bucketGet'; + const request = makeRequest({ + 'x-amz-optional-object-attributes': 'RestoreStatus', + }); + const results = prepareRequestContexts(apiMethod, request, sourceBucket, sourceObject, sourceVersionId); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].getAction(), 's3:ListBucket'); + }); + + it('should not request permission if the header does not exists', () => { + const apiMethod = 'bucketGet'; + const request = makeRequest({}); + const results = prepareRequestContexts(apiMethod, request, sourceBucket, sourceObject, sourceVersionId); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].getAction(), 's3:ListBucket'); + }); + }); + }); }); diff --git a/tests/unit/api/bucketGet.js b/tests/unit/api/bucketGet.js index ba166fd088..48befeafef 100644 --- a/tests/unit/api/bucketGet.js +++ b/tests/unit/api/bucketGet.js @@ -184,17 +184,17 @@ describe('bucketGet API', () => { async.waterfall([ next => bucketPut(authInfo, testPutBucketRequest, log, next), - (corsHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest1, undefined, log, next), - (resHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest2, undefined, log, next), - (resHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest3, undefined, log, next), - (resHeaders, next) => + (_, next) => bucketGet(authInfo, testGetRequest, log, next), - (result, corsHeaders, next) => parseString(result, next), + (result, _, next) => parseString(result, next), ], - (err, result) => { + (_, result) => { test.assertion(result); done(); }); @@ -216,13 +216,13 @@ describe('bucketGet API', () => { async.waterfall([ next => bucketPut(authInfo, testPutBucketRequest, log, next), - (corsHeaders, next) => objectPut(authInfo, testPutObjectRequest4, + (_, next) => objectPut(authInfo, testPutObjectRequest4, undefined, log, next), - (resHeaders, next) => bucketGet(authInfo, testGetRequest, + (_, next) => bucketGet(authInfo, testGetRequest, log, next), - (result, corsHeaders, next) => parseString(result, next), + (result, _, next) => parseString(result, next), ], - (err, result) => { + (_, result) => { assert.strictEqual(result.ListBucketResult.Contents[0].Key[0], testPutObjectRequest4.objectKey); done(); @@ -235,11 +235,11 @@ describe('bucketGet API', () => { async.waterfall([ next => bucketPut(authInfo, testPutBucketRequest, log, next), - (corsHeaders, next) => + (_, next) => bucketGet(authInfo, testGetRequest, log, next), - (result, corsHeaders, next) => parseString(result, next), + (result, _, next) => parseString(result, next), ], - (err, result) => { + (_, result) => { assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); done(); @@ -307,17 +307,17 @@ describe('bucketGet API V2', () => { async.waterfall([ next => bucketPut(authInfo, testPutBucketRequest, log, next), - (corsHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest1, undefined, log, next), - (resHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest2, undefined, log, next), - (resHeaders, next) => objectPut(authInfo, + (_, next) => objectPut(authInfo, testPutObjectRequest3, undefined, log, next), - (resHeaders, next) => + (_, next) => bucketGet(authInfo, testGetRequest, log, next), - (result, corsHeaders, next) => parseString(result, next), + (result, _, next) => parseString(result, next), ], - (err, result) => { + (_, result) => { // v2 requests should return 'KeyCount' in response const keyCount = Number.parseInt(result.ListBucketResult.KeyCount[0], 10); @@ -333,4 +333,111 @@ describe('bucketGet API V2', () => { }); }); }); + + describe('x-amz-optional-object-attributes header', () => { + it('should return an error if the header is empty', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = ''; + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], err => { + assert.strictEqual(err.is.InvalidArgument, true); + done(); + }); + }); + + it('should return an error for invalid optional attributes', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'InvalidAttribute'; + + async.series([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + next => bucketGet(authInfo, testGetRequest, log, next), + ], err => { + assert.strictEqual(err.is.InvalidArgument, true); + done(); + }); + }); + + it('should accept wildcard value', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-*'; + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], (err, result) => { + assert.strictEqual(err, null); + assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); + done(); + }); + }); + + it('should return an error for a mix of valid and invalid attributes', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'RestoreStatus,InvalidAttribute'; + + async.series([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + next => bucketGet(authInfo, testGetRequest, log, next), + ], err => { + assert.strictEqual(err.is.InvalidArgument, true); + done(); + }); + }); + + it('should handle attributes with leading/trailing whitespace', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = ' x-amz-meta-foo '; + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], + (err, result) => { + assert.strictEqual(err, null); + assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); + done(); + }); + }); + + it('should handle multiple valid attributes', done => { + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'RestoreStatus,x-amz-meta-foo,x-amz-meta-bar'; + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], + (err, result) => { + assert.strictEqual(err, null); + assert.strictEqual(result.ListBucketResult.$.xmlns, 'http://s3.amazonaws.com/doc/2006-03-01/'); + done(); + }); + }); + }); }); diff --git a/yarn.lock b/yarn.lock index c018a3859c..2dfce61f24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1451,32 +1451,37 @@ arraybuffer.prototype.slice@^1.0.4: optionalDependencies: ioctl "^2.0.2" -"arsenal@git+https://github.com/scality/Arsenal#8.2.39": - version "8.2.39" - resolved "git+https://github.com/scality/Arsenal#c81b221b6a1e6f283ae74a9e4cd791165adcaedb" +"arsenal@git+https://github.com/scality/Arsenal#8.2.4": + version "8.2.4" + resolved "git+https://github.com/scality/Arsenal#96ef6a3e26d7528f877300606586759f1da6d0cd" dependencies: - "@azure/identity" "^4.13.0" - "@azure/storage-blob" "^12.28.0" + "@azure/identity" "^4.5.0" + "@azure/storage-blob" "^12.25.0" + "@eslint/plugin-kit" "^0.2.3" "@js-sdsl/ordered-set" "^4.4.2" "@scality/hdclient" "^1.3.1" + "@types/async" "^3.2.24" + "@types/utf8" "^3.0.3" JSONStream "^1.3.5" - agentkeepalive "^4.6.0" + agentkeepalive "^4.5.0" ajv "6.12.3" async "~2.6.4" aws-sdk "^2.1691.0" backo "^1.1.0" base-x "3.0.8" base62 "^2.0.2" - debug "^4.4.3" + bson "^6.8.0" + debug "^4.3.7" + diskusage "^1.2.0" fcntl "github:scality/node-fcntl#0.3.0" httpagent scality/httpagent#1.1.0 - https-proxy-agent "^7.0.6" - ioredis "^5.8.1" + https-proxy-agent "^7.0.5" + ioredis "^5.4.1" ipaddr.js "^2.2.0" - joi "^18.0.1" + joi "^17.13.3" level "~5.0.1" level-sublevel "~6.6.5" - mongodb "^6.20.0" + mongodb "^6.11.0" node-forge "^1.3.1" prom-client "^15.1.3" simple-glob "^0.2.0" @@ -1490,37 +1495,32 @@ arraybuffer.prototype.slice@^1.0.4: optionalDependencies: ioctl "^2.0.2" -"arsenal@git+https://github.com/scality/Arsenal#8.2.4": - version "8.2.4" - resolved "git+https://github.com/scality/Arsenal#96ef6a3e26d7528f877300606586759f1da6d0cd" +"arsenal@git+https://github.com/scality/arsenal#8.2.44": + version "8.2.44" + resolved "git+https://github.com/scality/arsenal#960e77028b6eb614dde297e50a1530dcd9015f16" dependencies: - "@azure/identity" "^4.5.0" - "@azure/storage-blob" "^12.25.0" - "@eslint/plugin-kit" "^0.2.3" + "@azure/identity" "^4.13.0" + "@azure/storage-blob" "^12.28.0" "@js-sdsl/ordered-set" "^4.4.2" "@scality/hdclient" "^1.3.1" - "@types/async" "^3.2.24" - "@types/utf8" "^3.0.3" JSONStream "^1.3.5" - agentkeepalive "^4.5.0" + agentkeepalive "^4.6.0" ajv "6.12.3" async "~2.6.4" aws-sdk "^2.1691.0" backo "^1.1.0" base-x "3.0.8" base62 "^2.0.2" - bson "^6.8.0" - debug "^4.3.7" - diskusage "^1.2.0" + debug "^4.4.3" fcntl "github:scality/node-fcntl#0.3.0" httpagent scality/httpagent#1.1.0 - https-proxy-agent "^7.0.5" - ioredis "^5.4.1" + https-proxy-agent "^7.0.6" + ioredis "^5.8.1" ipaddr.js "^2.2.0" - joi "^17.13.3" + joi "^18.0.1" level "~5.0.1" level-sublevel "~6.6.5" - mongodb "^6.11.0" + mongodb "^6.20.0" node-forge "^1.3.1" prom-client "^15.1.3" simple-glob "^0.2.0" @@ -1573,6 +1573,11 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -2022,6 +2027,11 @@ color@^3.1.3: color-convert "^1.9.3" color-string "^1.6.0" +colorette@^2.0.7: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + colorspace@1.1.x: version "1.1.4" resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" @@ -2201,6 +2211,11 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" +dateformat@^4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" + integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== + dayjs@^1.11.10: version "1.11.15" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.15.tgz#fd7fd2db6fc92f08ffe4adc306756d45db00ada3" @@ -2461,6 +2476,13 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" +end-of-stream@^1.1.0: + version "1.4.5" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" + integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== + dependencies: + once "^1.4.0" + engine.io-client@~6.6.1: version "6.6.3" resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.6.3.tgz#815393fa24f30b8e6afa8f77ccca2f28146be6de" @@ -2968,6 +2990,11 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== +fast-copy@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-4.0.1.tgz#be5c74baede1a72adf8168df2dc56e842c77a00e" + integrity sha512-+uUOQlhsaswsizHFmEFAQhB3lSiQ+lisxl50N6ZP0wywlZeWsIESxSi9ftPEps8UGfiBzyYP7x27zA674WUvXw== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -3534,6 +3561,11 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +help-me@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/help-me/-/help-me-5.0.0.tgz#b1ebe63b967b74060027c2ac61f9be12d354a6f6" + integrity sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg== + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -4298,6 +4330,11 @@ joi@^18.0.1: "@hapi/topo" "^6.0.2" "@standard-schema/spec" "^1.0.0" +joycon@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -5454,6 +5491,11 @@ object.values@^1.2.0: define-properties "^1.2.1" es-object-atoms "^1.0.0" +on-exit-leak-free@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" + integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== + on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -5461,7 +5503,7 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" -once@1.x, once@^1.3.0, once@^1.4.0: +once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -5683,6 +5725,32 @@ pify@^4.0.0, pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pino-abstract-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz#b21e5f33a297e8c4c915c62b3ce5dd4a87a52c23" + integrity sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg== + dependencies: + split2 "^4.0.0" + +pino-pretty@^13.1.3: + version "13.1.3" + resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-13.1.3.tgz#2274cccda925dd355c104079a5029f6598d0381b" + integrity sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg== + dependencies: + colorette "^2.0.7" + dateformat "^4.6.3" + fast-copy "^4.0.0" + fast-safe-stringify "^2.1.1" + help-me "^5.0.0" + joycon "^3.1.1" + minimist "^1.2.6" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^3.0.0" + pump "^3.0.0" + secure-json-parse "^4.0.0" + sonic-boom "^4.0.1" + strip-json-comments "^5.0.2" + pkg-dir@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -5821,6 +5889,14 @@ pull-window@^2.1.4: dependencies: looper "^2.0.0" +pump@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" + integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -6117,6 +6193,11 @@ sax@>=0.6.0, sax@^1.2.4: "@smithy/signature-v4" "^2.1.1" axios "^1.3.4" +secure-json-parse@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-4.1.0.tgz#4f1ab41c67a13497ea1b9131bb4183a22865477c" + integrity sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA== + "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -6390,6 +6471,13 @@ socks@^2.8.3: ip-address "^9.0.5" smart-buffer "^4.2.0" +sonic-boom@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.2.0.tgz#e59a525f831210fa4ef1896428338641ac1c124d" + integrity sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww== + dependencies: + atomic-sleep "^1.0.0" + sorted-array-functions@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz#8605695563294dffb2c9796d602bd8459f7a0dd5" @@ -6452,6 +6540,11 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz#6d6e980c9df2b6fc905343a3b2d702a6239536c3" integrity sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg== +split2@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + sprintf-js@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" @@ -6640,6 +6733,11 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-5.0.3.tgz#b7304249dd402ee67fd518ada993ab3593458bcf" + integrity sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw== + strnum@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.0.5.tgz#40700b1b5bf956acdc755e98e90005d7657aaaea"