Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/artifact-permissions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,39 @@ describe('artifact-permissions', () => {
}
});

it('logs stderr when permission repair fails', () => {
const auditDir = makeTempDir();
let warnSpy: jest.SpyInstance | undefined;
try {
getuidSpy = jest.spyOn(process, 'getuid').mockReturnValue(1001);
warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
mockExecaSync.mockReturnValue({ stdout: '', stderr: 'no such image: agent:latest', exitCode: 1 });
fixArtifactPermissionsForRootless([auditDir], undefined, undefined, undefined, undefined);
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('no such image: agent:latest'));
} finally {
warnSpy?.mockRestore();
fs.rmSync(auditDir, { recursive: true, force: true });
}
});
Comment thread
Copilot marked this conversation as resolved.

it('logs exit code without stderr when stderr is empty', () => {
const auditDir = makeTempDir();
let warnSpy: jest.SpyInstance | undefined;
try {
getuidSpy = jest.spyOn(process, 'getuid').mockReturnValue(1001);
warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
mockExecaSync.mockReturnValue({ stdout: '', stderr: '', exitCode: 1 });
fixArtifactPermissionsForRootless([auditDir], undefined, undefined, undefined, undefined);
expect(warnSpy).toHaveBeenCalledWith(expect.stringMatching(/failed.*exit 1/i));
// Should NOT contain a colon suffix when stderr is empty
const warnCall = warnSpy.mock.calls.find(c => typeof c[0] === 'string' && /exit 1/.test(c[0]));
expect(warnCall?.[0]).not.toMatch(/exit 1\):/);
} finally {
warnSpy?.mockRestore();
fs.rmSync(auditDir, { recursive: true, force: true });
}
});
Comment thread
Copilot marked this conversation as resolved.

it('runs rootless permission repair with translated mount paths', () => {
const auditDir = makeTempDir();
try {
Expand Down
6 changes: 5 additions & 1 deletion src/artifact-permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
}

const existingDirs = dirs.filter(
(dir): dir is string => typeof dir === 'string' && dir.length > 0 && fs.existsSync(dir),

Check warning on line 37 in src/artifact-permissions.ts

View workflow job for this annotation

GitHub Actions / ESLint

Found existsSync from package "fs" with non literal argument at index 0

Check warning on line 37 in src/artifact-permissions.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 22)

Found existsSync from package "fs" with non literal argument at index 0

Check warning on line 37 in src/artifact-permissions.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 20)

Found existsSync from package "fs" with non literal argument at index 0
);
if (existingDirs.length === 0) {
return;
Expand Down Expand Up @@ -65,9 +65,9 @@
'--cap-add',
'FOWNER',
'-e',
`TUID=${uid}`,

Check warning on line 68 in src/artifact-permissions.ts

View workflow job for this annotation

GitHub Actions / ESLint

Avoid template literals with expressions in execa arguments. Pass arguments as separate array elements

Check warning on line 68 in src/artifact-permissions.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 22)

Avoid template literals with expressions in execa arguments. Pass arguments as separate array elements

Check warning on line 68 in src/artifact-permissions.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 20)

Avoid template literals with expressions in execa arguments. Pass arguments as separate array elements
'-e',
`TGID=${gid}`,

Check warning on line 70 in src/artifact-permissions.ts

View workflow job for this annotation

GitHub Actions / ESLint

Avoid template literals with expressions in execa arguments. Pass arguments as separate array elements

Check warning on line 70 in src/artifact-permissions.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 22)

Avoid template literals with expressions in execa arguments. Pass arguments as separate array elements

Check warning on line 70 in src/artifact-permissions.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 20)

Avoid template literals with expressions in execa arguments. Pass arguments as separate array elements
'-v',
mount,
imageRef,
Expand All @@ -79,7 +79,11 @@
);

if (typeof result.exitCode === 'number' && result.exitCode !== 0) {
logger.warn(`Rootless artifact permission repair failed for ${dir} (exit ${result.exitCode})`);
const stderr = result.stderr?.trim();
logger.warn(
`Rootless artifact permission repair failed for ${dir} (exit ${result.exitCode})` +
(stderr ? `: ${stderr}` : ''),
);
}
} catch (error) {
logger.warn(`Rootless artifact permission repair failed for ${dir}:`, error);
Expand Down
7 changes: 5 additions & 2 deletions src/artifact-preservation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
export function preserveIptablesAudit(workDir: string, auditDir?: string): void {
const iptablesAuditSrc = path.join(workDir, 'init-signal', 'iptables-audit.txt');
const targetAuditDir = auditDir || path.join(workDir, 'audit');
if (fs.existsSync(iptablesAuditSrc) && fs.existsSync(targetAuditDir)) {

Check warning on line 16 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / ESLint

Found existsSync from package "fs" with non literal argument at index 0

Check warning on line 16 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / ESLint

Found existsSync from package "fs" with non literal argument at index 0

Check warning on line 16 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 22)

Found existsSync from package "fs" with non literal argument at index 0

Check warning on line 16 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 22)

Found existsSync from package "fs" with non literal argument at index 0

Check warning on line 16 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 20)

Found existsSync from package "fs" with non literal argument at index 0

Check warning on line 16 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 20)

Found existsSync from package "fs" with non literal argument at index 0
try {
fs.copyFileSync(iptablesAuditSrc, path.join(targetAuditDir, 'iptables-audit.txt'));
fs.chmodSync(path.join(targetAuditDir, 'iptables-audit.txt'), 0o644);

Check warning on line 19 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / ESLint

Found chmodSync from package "fs" with non literal argument at index 0

Check warning on line 19 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 22)

Found chmodSync from package "fs" with non literal argument at index 0

Check warning on line 19 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 20)

Found chmodSync from package "fs" with non literal argument at index 0
logger.debug('Copied iptables audit state to audit directory');
} catch (error) {
logger.debug('Could not copy iptables audit file:', error);
Expand Down Expand Up @@ -55,7 +55,7 @@
}: PreserveDirectoryOptions): void {
if (runtimeDir) {
const targetDir = runtimeSubdir ? path.join(runtimeDir, runtimeSubdir) : runtimeDir;
if (!runtimeDirMustExist || fs.existsSync(targetDir)) {

Check warning on line 58 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / ESLint

Found existsSync from package "fs" with non literal argument at index 0

Check warning on line 58 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 22)

Found existsSync from package "fs" with non literal argument at index 0

Check warning on line 58 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 20)

Found existsSync from package "fs" with non literal argument at index 0
try {
execa.sync('chmod', ['-R', 'a+rX', targetDir]);
logger.info(`${availableLabel} available at: ${targetDir}`);
Expand All @@ -68,9 +68,9 @@

const sourceDir = path.join(workDir, workSubdir);
const destinationDir = path.join(os.tmpdir(), `${destinationBaseName}-${timestamp}`);
if (fs.existsSync(sourceDir) && fs.readdirSync(sourceDir).length > 0) {

Check warning on line 71 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / ESLint

Found readdirSync from package "fs" with non literal argument at index 0

Check warning on line 71 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / ESLint

Found existsSync from package "fs" with non literal argument at index 0

Check warning on line 71 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 22)

Found readdirSync from package "fs" with non literal argument at index 0

Check warning on line 71 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 22)

Found existsSync from package "fs" with non literal argument at index 0

Check warning on line 71 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 20)

Found readdirSync from package "fs" with non literal argument at index 0

Check warning on line 71 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 20)

Found existsSync from package "fs" with non literal argument at index 0
try {
fs.renameSync(sourceDir, destinationDir);

Check warning on line 73 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / ESLint

Found renameSync from package "fs" with non literal argument at index 0,1

Check warning on line 73 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 22)

Found renameSync from package "fs" with non literal argument at index 0,1

Check warning on line 73 in src/artifact-preservation.ts

View workflow job for this annotation

GitHub Actions / Build and Lint (Node 20)

Found renameSync from package "fs" with non literal argument at index 0,1
if (chmodPreservedDir) {
execa.sync('chmod', ['-R', 'a+rX', destinationDir]);
}
Expand Down Expand Up @@ -248,10 +248,13 @@
try {
fs.rmSync(chrootHomeDir, { recursive: true, force: true });
} catch (retryError) {
logger.warn('Failed to remove chroot home directory after permission repair:', retryError);
// Non-fatal: chroot-home will be cleaned by the post-step
// (install_copilot_cli.sh's sudo cleanup) or runner infrastructure.
logger.debug(`Could not remove chroot home directory after permission repair: ${chrootHomeDir}`, retryError);
}
} else {
logger.warn('Failed to remove chroot home directory:', error);
// Non-fatal: same reasoning — defer to post-step cleanup.
logger.debug('Failed to remove chroot home directory:', error);
}
}
}
Expand Down
Loading