Skip to content

Commit 25c6511

Browse files
Merge pull request #1480 from salesforcecli/sl/W-20013096
fix: Add a validate for output-dir flag W-20013096
2 parents 3cebebe + 7c92ff5 commit 25c6511

File tree

3 files changed

+18
-4
lines changed

3 files changed

+18
-4
lines changed

messages/retrieve.start.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,7 @@ This command expects the org to support source tracking. If it doesn't, you must
204204

205205
Starting in December 2025, this command will require that the target org use source tracking.
206206
Specifically, to use this command with a production org, scratch org created with the `--no-track-source` flag, or other non-source-tracking org, you must specify the metadata you want to retrieve with either the `--metadata`, `--source-dir`, or `--manifest` flag.
207+
208+
# outputDirOutsideProject
209+
210+
The output directory must be inside the current project. The path relative you provided %s is outside the project root.

src/commands/project/retrieve/start.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
import { rm } from 'node:fs/promises';
18-
import { dirname, join, resolve, sep } from 'node:path';
18+
import { dirname, join, relative, resolve, sep } from 'node:path';
1919
import * as fs from 'node:fs';
2020

2121
import { MultiStageOutput } from '@oclif/multi-stage-output';
@@ -204,6 +204,16 @@ export default class RetrieveMetadata extends SfCommand<RetrieveResultJson> {
204204
if (SfProject.getInstance()?.getPackageNameFromPath(resolvedTargetDir)) {
205205
throw messages.createError('retrieveTargetDirOverlapsPackage', [flags['output-dir']]);
206206
}
207+
208+
// Ensure --output-dir is inside the current project directory
209+
const project = await getOptionalProject();
210+
if (project) {
211+
const rel = relative(project.getPath(), resolvedTargetDir);
212+
if (rel.startsWith('..')) {
213+
// resolvedTargetDir is outside the project path
214+
throw messages.createError('outputDirOutsideProject', [flags['output-dir']]);
215+
}
216+
}
207217
}
208218
const format = flags['target-metadata-dir'] ? 'metadata' : 'source';
209219
const zipFileName = flags['zip-file-name'] ?? DEFAULT_ZIP_FILE_NAME;

test/commands/retrieve/start.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,9 @@ describe('project retrieve start', () => {
157157
// @ts-ignore
158158
const renameStub = $$.SANDBOX.stub(RetrieveMetadata.prototype, 'moveResultsForRetrieveTargetDir').resolves();
159159

160-
const sourcepath = ['somepath'];
160+
const outputDir = resolve(expectedDirectoryPath, '..', 'retrieveOutput');
161161
const metadata = ['ApexClass:MyClass'];
162-
const result = await RetrieveMetadata.run(['--output-dir', sourcepath[0], '--metadata', metadata[0], '--json']);
162+
const result = await RetrieveMetadata.run(['--output-dir', outputDir, '--metadata', metadata[0], '--json']);
163163
expect(result).to.deep.equal(expectedResults);
164164
ensureCreateComponentSetArgs({
165165
sourcepath: undefined,
@@ -168,7 +168,7 @@ describe('project retrieve start', () => {
168168
metadataEntries: ['ApexClass:MyClass'],
169169
},
170170
});
171-
ensureRetrieveArgs({ output: resolve(sourcepath[0]), format: 'source' });
171+
ensureRetrieveArgs({ output: resolve(outputDir), format: 'source' });
172172
expect(renameStub.calledOnce).to.be.true;
173173
});
174174

0 commit comments

Comments
 (0)