UNPKG

sfdx-hardis

Version:

Swiss-army-knife Toolbox for Salesforce. Allows you to define a complete CD/CD Pipeline. Orchestrate base commands and assist users with interactive wizards

110 lines 4.78 kB
/* jscpd:ignore-start */ import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; import { Messages } from '@salesforce/core'; import fs from 'fs-extra'; import { glob } from 'glob'; import * as psl from 'psl'; import sortArray from 'sort-array'; import * as url from 'url'; import { catchMatches, generateReports, uxLog } from '../../../../common/utils/index.js'; import { GLOB_IGNORE_PATTERNS } from '../../../../common/utils/projectUtils.js'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('sfdx-hardis', 'org'); export default class RemoteSites extends SfCommand { static title = 'Audit Remote Sites'; static description = messages.getMessage('auditRemoteSites'); static examples = ['$ sf hardis:project:audit:remotesites']; // public static args = [{name: 'file'}]; static flags = { // flag with a value (-n, --name=VALUE) debug: Flags.boolean({ char: 'd', default: false, description: messages.getMessage('debugMode'), }), websocket: Flags.string({ description: messages.getMessage('websocket'), }), skipauth: Flags.boolean({ description: 'Skip authentication check when a default username is required', }), }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default static requiresProject = true; /* jscpd:ignore-end */ matchResults = []; async run() { const pattern = '**/*.{remoteSite-meta.xml,remoteSite}'; const catchers = [ { type: '', subType: '', regex: /<url>(.*?)<\/url>/gim, detail: [ { name: 'url', regex: /<url>(.*?)<\/url>/gims }, { name: 'active', regex: /<isActive>(.*?)<\/isActive>/gims }, { name: 'description', regex: /<description>(.*?)<\/description>/gimsu, }, ], }, ]; const remoteSiteSettingsFiles = await glob(pattern, { ignore: GLOB_IGNORE_PATTERNS }); this.matchResults = []; uxLog(this, `Browsing ${remoteSiteSettingsFiles.length} files`); // Loop in files for (const file of remoteSiteSettingsFiles) { const fileText = await fs.readFile(file, 'utf8'); // Loop on criteria to find matches in this file for (const catcher of catchers) { const catcherMatchResults = await catchMatches(catcher, file, fileText, this); this.matchResults.push(...catcherMatchResults); } } // Format result const result = this.matchResults.map((item) => { return { name: item.fileName.replace('.remoteSite-meta.xml', '').replace('.remoteSite', ''), fileName: item.fileName, nameSpace: item.fileName.includes('__') ? item.fileName.split('__')[0] : 'Custom', matches: item.matches, url: item.detail?.url ? item.detail.url[0] : '', active: item.detail?.active ? 'yes' : 'no', description: item.detail?.description ? item.detail.description[0] : '', protocol: item.detail.url[0].includes('https') ? 'HTTPS' : 'HTTP', domain: psl.parse(new url.URL(item.detail.url[0]).hostname)?.domain || 'Domain not found', }; }); // Sort array const resultSorted = sortArray(result, { by: ['protocol', 'domain', 'name', 'active', 'description'], order: ['asc', 'asc', 'asc', 'desc', 'asc'], }); // Display as table const resultsLight = JSON.parse(JSON.stringify(resultSorted)); console.table(resultsLight.map((item) => { delete item.fileName; delete item.detail; delete item.matches; return item; })); // Export into csv & excel file const columns = [ { key: 'protocol', header: 'Protocol' }, { key: 'domain', header: 'Domain' }, { key: 'name', header: 'Name' }, { key: 'url', header: 'URL' }, { key: 'active', header: 'Active' }, { key: 'description', header: 'Description' }, ]; const reportFiles = await generateReports(resultSorted, columns, this); // Return an object to be displayed with --json return { outputString: 'Processed callIns and callOuts audit', result: resultSorted, reportFiles, }; } } //# sourceMappingURL=remotesites.js.map