Skip to content

Commit

Permalink
fix: resolve the actual paths for base and target
Browse files Browse the repository at this point in the history
Signed-off-by: blam <ben@blam.sh>
  • Loading branch information
benjdlambert committed Feb 22, 2024
1 parent 1ab0d3e commit 78f892b
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 1 deletion.
63 changes: 63 additions & 0 deletions packages/backend-common/src/paths.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { createMockDirectory } from '@backstage/backend-test-utils';
import { resolveSafeChildPath } from './paths';

describe('paths', () => {
describe('resolveSafeChildPath', () => {
const mockDir = createMockDirectory();
const secondDirectory = createMockDirectory();

const workspacePath = mockDir.resolve('workspace');

beforeEach(() => {
mockDir.setContent({
[`${workspacePath}/README.md`]: '### README.md',
});
secondDirectory.setContent({
[`index.md`]: '### index.md',
});
});

it('should throw an error if the path is outside of the base path', () => {
expect(() =>
resolveSafeChildPath(workspacePath, secondDirectory.path),
).toThrow(
'Relative path is not allowed to refer to a directory outside its parent',
);
});

it('should resolve to the full path if the target is inside the directory', () => {
expect(resolveSafeChildPath(workspacePath, './README.md')).toEqual(
`${workspacePath}/README.md`,
);
});

it('should throw an error if the path is a symlink to a directory outside of the base path', () => {
mockDir.addContent({
[`${workspacePath}/symlink`]: ({ symlink }) =>
symlink(secondDirectory.path),
});

expect(() =>
resolveSafeChildPath(workspacePath, './symlink/index.md'),
).toThrow(
'Relative path is not allowed to refer to a directory outside its parent',
);
});
});
});
14 changes: 13 additions & 1 deletion packages/backend-common/src/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { isChildPath } from '@backstage/cli-common';
import { NotAllowedError } from '@backstage/errors';
import { resolve as resolvePath } from 'path';
import { realpathSync as realPath } from 'fs';

/** @internal */
export const packagePathMocks = new Map<
Expand Down Expand Up @@ -64,7 +65,7 @@ export function resolvePackagePath(name: string, ...paths: string[]) {
export function resolveSafeChildPath(base: string, path: string): string {
const targetPath = resolvePath(base, path);

if (!isChildPath(base, targetPath)) {
if (!isChildPath(resolveRealPath(base), resolveRealPath(targetPath))) {
throw new NotAllowedError(
'Relative path is not allowed to refer to a directory outside its parent',
);
Expand All @@ -73,5 +74,16 @@ export function resolveSafeChildPath(base: string, path: string): string {
return targetPath;
}

function resolveRealPath(path: string): string {
try {
return realPath(path);
} catch (ex) {
if (ex.code !== 'ENOENT') {
throw ex;
}
}

return path;
}
// Re-export isChildPath so that backend packages don't need to depend on cli-common
export { isChildPath };

0 comments on commit 78f892b

Please sign in to comment.