fix(fleet): contain credential trust roots
AMD1213-B4: reject symlinked auth ancestry and group/world-readable credential artifacts before composition can write.
This commit is contained in:
@@ -375,8 +375,15 @@ function resolveCredential(
|
||||
userHome: string,
|
||||
seatHome: string,
|
||||
): Pick<FleetLaunchComposition, 'bundle' | 'credential'> {
|
||||
const authRoot = join(userHome, 'auth', profile.harness);
|
||||
assertRealDirectory(userHome, 'user Mosaic root');
|
||||
const realUserHome = realpathSync(userHome);
|
||||
const authDirectory = join(userHome, 'auth');
|
||||
assertRealDirectory(authDirectory, 'auth directory');
|
||||
const authRoot = join(authDirectory, profile.harness);
|
||||
assertRealDirectory(authRoot, `${profile.harness} auth root`);
|
||||
const resolvedAuthRoot = realpathSync(authRoot);
|
||||
assertContained(realUserHome, resolvedAuthRoot, `${profile.harness} auth root`);
|
||||
|
||||
const bundlePath = join(authRoot, profile.bundle);
|
||||
const bundleInfo = lstatIfPresent(bundlePath);
|
||||
if (!bundleInfo) {
|
||||
@@ -395,7 +402,7 @@ function resolveCredential(
|
||||
const detail = error instanceof Error ? error.message : String(error);
|
||||
throw new FleetLaunchError('COMPOSITION_FAILED', `credential bundle cannot resolve: ${detail}`);
|
||||
}
|
||||
assertContained(realpathSync(authRoot), resolvedBundleDir, 'credential bundle');
|
||||
assertContained(resolvedAuthRoot, resolvedBundleDir, 'credential bundle');
|
||||
assertRealDirectory(resolvedBundleDir, 'resolved credential bundle');
|
||||
|
||||
const credentialTarget = join(resolvedBundleDir, CREDENTIAL_FILES[profile.harness]);
|
||||
@@ -406,8 +413,15 @@ function resolveCredential(
|
||||
`bundle credential must be a real, non-symlink credential file: ${credentialTarget}`,
|
||||
);
|
||||
}
|
||||
if ((credentialInfo.mode & 0o077) !== 0) {
|
||||
throw new FleetLaunchError(
|
||||
'COMPOSITION_FAILED',
|
||||
`bundle credential file must not grant group or other permissions: ${credentialTarget}`,
|
||||
);
|
||||
}
|
||||
// Fleet launches and credential bundles share one operating-system user; ownership validation is deferred.
|
||||
const resolvedCredential = realpathSync(credentialTarget);
|
||||
assertContained(realpathSync(authRoot), resolvedCredential, 'bundle credential');
|
||||
assertContained(resolvedAuthRoot, resolvedCredential, 'bundle credential');
|
||||
|
||||
const credentialLink = join(seatHome, CREDENTIAL_FILES[profile.harness]);
|
||||
const seatInfo = lstatIfPresent(credentialLink);
|
||||
|
||||
Reference in New Issue
Block a user