fix(core): authorize relative external paths (#37689)

This commit is contained in:
Kit Langton 2026-07-18 19:55:18 -04:00 committed by GitHub
commit c310ef82f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 52 additions and 17 deletions

View file

@ -12,8 +12,8 @@ export const Kind = Schema.Literals(["file", "directory"])
export type Kind = typeof Kind.Type
/**
* Mutation paths do not accept project references. Relative paths must stay
* inside the active Location. Absolute paths outside it require separate
* Mutation paths do not accept project references. Relative paths resolve
* from the active Location. Paths outside it require separate
* `external_directory` approval.
*/
export const ResolveInput = Schema.Struct({
@ -25,7 +25,7 @@ export type ResolveInput = typeof ResolveInput.Type
export class PathError extends Schema.TaggedErrorClass<PathError>()("LocationMutation.PathError", {
path: Schema.String,
reason: Schema.Literals(["relative_escape", "location_escape", "non_directory_ancestor"]),
reason: Schema.Literals(["location_escape", "non_directory_ancestor"]),
}) {}
export interface ExternalDirectoryAuthorization {
@ -53,9 +53,9 @@ export interface Target {
export interface Interface {
/**
* Resolve a path and derive its permission resources. Relative paths must
* stay inside the Location. Absolute paths outside it require separate
* `external_directory` approval. This does not approve the mutation.
* Resolve a path and derive its permission resources. Relative paths resolve
* from the Location. Paths outside it require separate `external_directory`
* approval. This does not approve the mutation.
*/
readonly resolve: (input: ResolveInput) => Effect.Effect<Target, PathError | FSUtil.Error>
}
@ -120,10 +120,8 @@ const layer = Layer.effect(
})
const resolve = Effect.fn("LocationMutation.resolve")(function* (input: ResolveInput) {
const relative = !path.isAbsolute(input.path)
const absolute = path.resolve(location.directory, input.path)
const lexicallyInternal = FSUtil.contains(location.directory, absolute)
if (relative && !lexicallyInternal) return yield* new PathError({ path: input.path, reason: "relative_escape" })
const resolved = yield* resolvePath(absolute)
if (lexicallyInternal && !FSUtil.contains(locationRoot, resolved.canonical)) {
@ -146,10 +144,7 @@ const layer = Layer.effect(
directory: externalDirectory,
resource: externalResource,
save: slash(
path.join(
(yield* Project.root(fs, AbsolutePath.make(externalDirectory))) ?? externalDirectory,
"*",
),
path.join((yield* Project.root(fs, AbsolutePath.make(externalDirectory))) ?? externalDirectory, "*"),
),
}
: undefined,