From 42d9409a7b46e5e6dd5f466a2e1235d83aabe152 Mon Sep 17 00:00:00 2001 From: Shane McLaughlin Date: Mon, 18 Sep 2023 15:57:44 -0500 Subject: [PATCH] fix: disallow undefined xml (#757) * fix: disallow undefined xml * test: add ut and require `type` to be present * style: one import from sdr * chore: lockfile dedupe * test: more coverage and cases for typeguard and SC * fix: bump SDR --------- Co-authored-by: Cristian Dominguez --- package.json | 2 +- src/utils/previewOutput.ts | 4 +-- src/utils/types.ts | 15 ++++++-- test/utils/types.test.ts | 72 ++++++++++++++++++++++++++++++++++++++ yarn.lock | 37 +++++++------------- 5 files changed, 100 insertions(+), 30 deletions(-) create mode 100644 test/utils/types.test.ts diff --git a/package.json b/package.json index 36d301b6..d2e5e50b 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "@salesforce/core": "^5.2.7", "@salesforce/kit": "^3.0.9", "@salesforce/sf-plugins-core": "^3.1.22", - "@salesforce/source-deploy-retrieve": "^9.7.13", + "@salesforce/source-deploy-retrieve": "^9.7.15", "@salesforce/source-tracking": "^4.2.12", "chalk": "^4.1.2", "shelljs": "^0.8.5", diff --git a/src/utils/previewOutput.ts b/src/utils/previewOutput.ts index 62e088cc..158957e0 100644 --- a/src/utils/previewOutput.ts +++ b/src/utils/previewOutput.ts @@ -21,7 +21,7 @@ import { import { filePathsFromMetadataComponent } from '@salesforce/source-deploy-retrieve/lib/src/utils/filePathGenerator'; import { SourceTracking } from '@salesforce/source-tracking'; -import { isSourceComponent } from './types'; +import { isSourceComponentWithXml } from './types'; Messages.importMessagesDirectory(__dirname); const messages = Messages.loadMessages('@salesforce/plugin-deploy-retrieve', 'previewMessages'); @@ -64,7 +64,7 @@ const resolvePaths = (filenames: string[]): Array ({ fullName: sc.fullName, type: sc.type.name, path: ensureAbsolutePath(sc.xml) })); // dedupe by xml path return Array.from(new Map(sourceComponents.map((sc) => [sc.path, sc])).values()); diff --git a/src/utils/types.ts b/src/utils/types.ts index 4dfec7cc..0e316ad6 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -101,8 +101,19 @@ export type Formatter = { }; /** validates source component with fullname, type, and xml props */ -export const isSourceComponent = (sc: unknown): sc is SourceComponent & { xml: string } => - isObject(sc) && 'fullName' in sc && 'type' in sc && 'xml' in sc; +export const isSourceComponent = (sc: unknown): sc is SourceComponent => + isObject(sc) && + 'type' in sc && + typeof sc.type === 'object' && + sc.type !== null && + 'name' in sc.type && + typeof sc.type.name === 'string' && + 'fullName' in sc && + // (typeof sc.fullName === 'string' || typeof sc.fullName === 'function'); + typeof sc.fullName === 'string'; + +export const isSourceComponentWithXml = (sc: unknown): sc is SourceComponent & { xml: string } => + isSourceComponent(sc) && 'xml' in sc && typeof sc.xml === 'string'; export const isSdrFailure = (fileResponse: FileResponse): fileResponse is FileResponseFailure => fileResponse.state === ComponentStatus.Failed; diff --git a/test/utils/types.test.ts b/test/utils/types.test.ts new file mode 100644 index 00000000..19fa572f --- /dev/null +++ b/test/utils/types.test.ts @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2023, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import { expect, config } from 'chai'; +import { SourceComponent, RegistryAccess } from '@salesforce/source-deploy-retrieve'; +import { isSourceComponent, isSourceComponentWithXml } from '../../src/utils/types'; + +config.truncateThreshold = 0; + +const reg = new RegistryAccess(); +const type = reg.getTypeByName('ApexClass'); + +describe('isSourceComponent (type guard)', () => { + describe('good', () => { + it('full, correct definition', () => { + expect({ fullName: 'foo', type, xml: 'fooXml', content: 'fooContent' }).to.satisfy(isSourceComponent); + }); + it('SC constructed with xml', () => { + expect(new SourceComponent({ name: 'foo', type, xml: 'classes/foo.cls' })).to.satisfy(isSourceComponent); + }); + it('SC constructed with no xml', () => { + const sc = new SourceComponent({ name: 'foo', type }); + // console.log(sc); + // console.log(typeof sc.fullName); + expect(sc).to.satisfy(isSourceComponent); + }); + }); + describe('bad', () => { + it('object is undefined', () => { + expect(undefined).to.not.satisfy(isSourceComponent); + }); + it('empty object', () => { + expect({}).to.not.satisfy(isSourceComponent); + }); + + it('object.type is set to undefined', () => { + expect({ fullName: 'foo', type: undefined, xml: 'fooXml' }).to.not.satisfy(isSourceComponent); + }); + }); +}); + +describe('isSourceComponentWithXml (type guard)', () => { + describe('good', () => { + it('full, correct definition', () => { + expect({ fullName: 'foo', type, xml: 'fooXml', content: 'fooContent' }).to.satisfy(isSourceComponentWithXml); + }); + it('SC constructed with xml', () => { + expect(new SourceComponent({ name: 'foo', type, xml: 'classes/foo.cls' })).to.satisfy(isSourceComponentWithXml); + }); + }); + describe('bad', () => { + it('object is undefined', () => { + expect(undefined).to.not.satisfy(isSourceComponentWithXml); + }); + it('empty object', () => { + expect({}).to.not.satisfy(isSourceComponentWithXml); + }); + it('object.xml is undefined', () => { + expect({ fullName: 'foo', type: 'fooType', content: 'fooContent' }).to.not.satisfy(isSourceComponentWithXml); + }); + it('object.type is set to undefined', () => { + expect({ fullName: 'foo', type: undefined, xml: 'fooXml' }).to.not.satisfy(isSourceComponentWithXml); + }); + it('SC constructed with no xml', () => { + expect(new SourceComponent({ name: 'foo', type })).to.not.satisfy(isSourceComponentWithXml); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 29ed1378..5c10eb1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -956,15 +956,15 @@ strip-ansi "6.0.1" ts-retry-promise "^0.7.0" -"@salesforce/core@^5.2.0", "@salesforce/core@^5.2.6", "@salesforce/core@^5.2.7": - version "5.2.9" - resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-5.2.9.tgz#812478061d766cdff28f7a0e8abefc9de562465a" - integrity sha512-ZWNxHnCPGT1pcJe1bjeRjd8VAeHELK4fftt/2WO+ZsPFHZnzmdowz2Th407v04et+uIzL0Z6+qOaRY/bZr5tLA== +"@salesforce/core@^5.2.0", "@salesforce/core@^5.2.6", "@salesforce/core@^5.2.7", "@salesforce/core@^5.2.9": + version "5.2.10" + resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-5.2.10.tgz#7f4824ddcef108b6ccec24434260c620de39012e" + integrity sha512-Xj1QRajmHWgl0ahivjKFGKJlGXwe9yFOZ3PwF91qEupGbO74XrCJ8OUM7EVlk53LKy9LlPZQFuy2ATX9MyEDKA== dependencies: "@salesforce/kit" "^3.0.11" "@salesforce/schemas" "^1.6.0" "@salesforce/ts-types" "^2.0.7" - "@types/semver" "^7.5.1" + "@types/semver" "^7.5.2" ajv "^8.12.0" change-case "^4.1.2" faye "^1.4.0" @@ -1121,12 +1121,12 @@ chalk "^4" inquirer "^8.2.5" -"@salesforce/source-deploy-retrieve@^9.7.13", "@salesforce/source-deploy-retrieve@^9.7.2", "@salesforce/source-deploy-retrieve@^9.7.8": - version "9.7.13" - resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-9.7.13.tgz#85777222e157a621eb00fcef0487c682b7e3d5f7" - integrity sha512-QRhdepll3+ED9w4clI/RvcSLtuSbTzFPZenzrvJ2R7Pg5gXRZQIJrphEkAnY7oteVBBUxPVdYgbeuQxeoi98dg== +"@salesforce/source-deploy-retrieve@^9.7.15", "@salesforce/source-deploy-retrieve@^9.7.2", "@salesforce/source-deploy-retrieve@^9.7.8": + version "9.7.15" + resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-9.7.15.tgz#a891e95101816a7e33854bc42ba21fafbdb1728e" + integrity sha512-av9Ojd9B/uegLNJth41aVPvAVPh8N0CllzfiDG5qbtDXW7WV/bqgWjiiiGc1U6RWnJ9OmQZNL/EjaSX0UPhiLA== dependencies: - "@salesforce/core" "^5.2.7" + "@salesforce/core" "^5.2.9" "@salesforce/kit" "^3.0.11" "@salesforce/ts-types" "^2.0.7" fast-levenshtein "^3.0.0" @@ -1558,12 +1558,7 @@ dependencies: "@types/node" "*" -"@types/semver@^7.3.12": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" - integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== - -"@types/semver@^7.5.1": +"@types/semver@^7.3.12", "@types/semver@^7.5.1", "@types/semver@^7.5.2": version "7.5.2" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.2.tgz#31f6eec1ed7ec23f4f05608d3a2d381df041f564" integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw== @@ -6607,15 +6602,7 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pino-abstract-transport@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz#cc0d6955fffcadb91b7b49ef220a6cc111d48bb3" - integrity sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA== - dependencies: - readable-stream "^4.0.0" - split2 "^4.0.0" - -pino-abstract-transport@v1.1.0: +pino-abstract-transport@^1.0.0, pino-abstract-transport@v1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz#083d98f966262164504afb989bccd05f665937a8" integrity sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==