diff --git a/src/resolve/manifestResolver.ts b/src/resolve/manifestResolver.ts index d4680229f4..db28dd4423 100644 --- a/src/resolve/manifestResolver.ts +++ b/src/resolve/manifestResolver.ts @@ -5,8 +5,9 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import { XMLParser } from 'fast-xml-parser'; +import { XMLParser, XMLValidator } from 'fast-xml-parser'; import { ensureArray } from '@salesforce/kit'; +import { SfError } from '@salesforce/core'; import { MetadataType, RegistryAccess } from '../registry'; import { NodeFSTreeContainer, TreeContainer } from './treeContainers'; import { MetadataComponent } from './types'; @@ -52,15 +53,22 @@ export class ManifestResolver { public async resolve(manifestPath: string): Promise { const components: MetadataComponent[] = []; - const file = await this.tree.readFile(manifestPath); - + const file = (await this.tree.readFile(manifestPath)).toString(); + const validateResult = XMLValidator.validate(file); + if (validateResult !== true) { + const error = new SfError( + `Invalid manifest file: ${manifestPath}. ${validateResult.err.code}: ${validateResult.err.msg} (Line ${validateResult.err.line} Column ${validateResult.err.col})`, + 'InvalidManifest' + ); + error.setData(validateResult.err); + throw error; + } const parser = new XMLParser({ stopNodes: ['version'], // In order to preserve the .0 on the apiVersion skip parsing it numberParseOptions: { leadingZeros: false, hex: false, skipLike: /\.0$/ }, }); - const parsedManifest: ParsedPackageManifest = (parser.parse(String(file)) as { Package: ParsedPackageManifest }) - .Package; + const parsedManifest: ParsedPackageManifest = (parser.parse(file) as { Package: ParsedPackageManifest }).Package; const packageTypeMembers = ensureArray(parsedManifest.types); const apiVersion = parsedManifest.version; diff --git a/test/resolve/manifestResolver.test.ts b/test/resolve/manifestResolver.test.ts index d098c1b8f0..6fce8bf9bb 100644 --- a/test/resolve/manifestResolver.test.ts +++ b/test/resolve/manifestResolver.test.ts @@ -5,7 +5,7 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import { expect } from 'chai'; +import { assert, expect } from 'chai'; import { createSandbox } from 'sinon'; import { ManifestResolver, @@ -80,6 +80,33 @@ describe('ManifestResolver', () => { expect(result.components).to.deep.equal(expected); }); + it('should throw on invalid xml', async () => { + const badManifest: VirtualFile = { + name: 'bad-package.xml', + data: Buffer.from(` + + < + + + + 52.0 + \n`), + }; + const tree = new VirtualTreeContainer([ + { + dirPath: '.', + children: [badManifest], + }, + ]); + const resolver = new ManifestResolver(tree); + try { + await resolver.resolve(badManifest.name); + expect(true, 'expected invalid types definition error').to.be.false; + } catch (error) { + assert(error instanceof Error); + expect(error.name).to.equal('InvalidManifest'); + } + }); it('should throw when type is empty', async () => { const badManifest: VirtualFile = { name: 'bad-package.xml',