Skip to content

Commit 9c7634b

Browse files
authored
[codex-security] Support nested Git repositories in scan snapshots (#116)
* [codex-security] support nested Git repositories in scan snapshots * [codex-security] simplify nested repository snapshots * fix: preserve nested repositories in remediation checkouts
1 parent fb2a16f commit 9c7634b

2 files changed

Lines changed: 51 additions & 4 deletions

File tree

sdk/typescript/_bundled_plugin/scripts/workbench_target.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,13 @@ def worktree_content_digest_for_context(
143143
b"untracked-content",
144144
os.fsencode(os.readlink(path)),
145145
)
146+
elif stat.S_ISDIR(metadata.st_mode):
147+
update_digest_field(digest, b"untracked-kind", b"directory")
148+
update_digest_field(
149+
digest,
150+
b"untracked-content",
151+
directory_content_digest(path.resolve()).encode(),
152+
)
146153
elif stat.S_ISREG(metadata.st_mode):
147154
content_digest = hashlib.sha256()
148155
content_size = 0
@@ -419,7 +426,13 @@ def copy_git_worktree_files(source: Path, destination: Path, excluded: tuple[Pat
419426
destination_path.symlink_to(os.readlink(source_path))
420427
elif stat.S_ISREG(metadata.st_mode):
421428
shutil.copy2(source_path, destination_path, follow_symlinks=False)
422-
elif not stat.S_ISDIR(metadata.st_mode):
429+
elif stat.S_ISDIR(metadata.st_mode):
430+
nested_git_dir = git_output(source_path, "rev-parse", "--absolute-git-dir")
431+
if nested_git_dir is None:
432+
raise SystemExit(f"Could not inspect nested Git working tree: {relative}")
433+
copy_git_worktree_files(source_path, destination_path, excluded)
434+
(destination_path / ".git").write_text(f"gitdir: {nested_git_dir}\n")
435+
else:
423436
raise SystemExit(f"Unsupported Git working-tree file type: {relative}")
424437
copied_target = destination if pathspec == "." else destination / pathspec
425438
copied_target.mkdir(parents=True, exist_ok=True)

sdk/typescript/tests-ts/scan-recovery.test.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ async function workbench(fixture: ScanFixture, args: readonly string[]) {
115115
}
116116

117117
async function startDraftScan(
118-
repositoryKind: "directory" | "clean" | "dirty" = "directory",
118+
repositoryKind: "directory" | "clean" | "dirty" | "nested" = "directory",
119119
): Promise<ScanFixture> {
120120
const root = await realpath(
121121
await mkdtemp(join(tmpdir(), "codex-security-scan-recovery-")),
@@ -153,6 +153,15 @@ async function startDraftScan(
153153
if (repositoryKind === "dirty") {
154154
await writeFile(join(target, "src", "extract.py"), "# changed fixture\n");
155155
}
156+
if (repositoryKind === "nested") {
157+
const nested = join(target, "nested");
158+
await mkdir(nested);
159+
await writeFile(join(nested, "source.py"), "# nested fixture\n");
160+
const initialized = spawnSync("git", ["init", "--quiet", nested], {
161+
encoding: "utf8",
162+
});
163+
expect(initialized.status, initialized.stderr).toBe(0);
164+
}
156165
}
157166

158167
const fixture: ScanFixture = {
@@ -246,8 +255,8 @@ describe("malformed scan artifact recovery", () => {
246255
});
247256
});
248257

249-
test("returns authoritative clean and dirty Git target contracts", async () => {
250-
for (const kind of ["clean", "dirty"] as const) {
258+
test("returns authoritative clean, dirty, and nested Git target contracts", async () => {
259+
for (const kind of ["clean", "dirty", "nested"] as const) {
251260
const fixture = await startDraftScan(kind);
252261
const registration = fixture.registration;
253262
const contract = registration["contract"] as {
@@ -276,6 +285,31 @@ describe("malformed scan artifact recovery", () => {
276285
/^codex-security-snapshot\/v1:sha256:[a-f0-9]{64}$/,
277286
);
278287
}
288+
if (kind === "nested") {
289+
const copied = spawnSync(
290+
fixture.python,
291+
[
292+
"-I",
293+
"-B",
294+
"-c",
295+
[
296+
"import sys",
297+
"from pathlib import Path",
298+
"sys.path.insert(0, sys.argv[1])",
299+
"import workbench_target as target",
300+
"source = Path(sys.argv[2])",
301+
"checkout = target.copy_git_worktree_files(source, Path(sys.argv[3]), ())",
302+
"git_dir = Path(target.git_output(source, 'rev-parse', '--absolute-git-dir'))",
303+
"assert target.worktree_content_digest_for_context(checkout, '.', git_dir=git_dir, work_tree=checkout) == target.worktree_content_digest(source)",
304+
].join("\n"),
305+
join(PLUGIN_ROOT, "scripts"),
306+
fixture.repository,
307+
join(fixture.stateDir, "checkout"),
308+
],
309+
{ encoding: "utf8" },
310+
);
311+
expect(copied.status, copied.stderr).toBe(0);
312+
}
279313
}
280314
});
281315

0 commit comments

Comments
 (0)