- Package Name: azure-containerapps-sandbox
- Package Version: 0.1.0b4
- Operating System: Windows 11 (build 26220.8764)
- Python Version: 3.12
Describe the bug
FileInfo loses type information that the service actually returns. The data-plane GET .../files/stat response carries isSymlink and symlinkTarget, and FileInfo has no field for either — so a caller using the typed surface cannot tell a symlink from a regular file. isDir is also mis-mapped, and mode is annotated as str but arrives as an int.
Three defects, in one model:
| Wire field |
Value returned by the service |
FileInfo |
isSymlink |
true for a symlink |
absent — no field at all |
symlinkTarget |
e.g. /etc/hostname |
absent — no field at all |
isDir |
true for a directory |
is_directory is False |
mode |
420 / 511 / 493 (int) |
typed str | None, populated with the int |
This matters beyond tidiness because read_file follows symlinks. Reading a path that happens to be a symlink returns the target's contents, from anywhere in the guest filesystem. A caller that wants to refuse symlinked paths has the information available on the wire and no supported way to reach it — mode is permission bits only (0o644 / 0o777 / 0o755, no S_IFLNK), and is_directory cannot be trusted.
To Reproduce
- Create a sandbox and seed a regular file, a symlink and a directory:
sc.write_file("/work/real.txt", "REAL-CONTENT", create_dirs=True)
sc.exec("ln -sf /etc/hostname /work/link-out.txt; mkdir -p /work/sub", working_directory="/work")
- Stat each through the typed surface:
for p in ["/work/real.txt", "/work/link-out.txt", "/work/sub"]:
f = sc.stat_file(p)
print(p, f.size, f.is_directory, repr(f.mode))
/work/real.txt 12 False 420
/work/link-out.txt 13 False 511
/work/sub 4096 False 493
/work/sub is a directory and reports is_directory=False; the symlink is indistinguishable from the regular file; every mode is an int despite the str | None annotation.
- Compare against the raw response for the same three paths:
{"name":"real.txt", "size":12, "mode":420, "isDir":false, "isSymlink":false, "modifiedTime":1786404028}
{"name":"link-out.txt", "size":13, "mode":511, "isDir":false, "isSymlink":true, "symlinkTarget":"/etc/hostname", "modifiedTime":1786404028}
{"name":"sub", "size":4096, "mode":493, "isDir":true, "isSymlink":false, "modifiedTime":1786404028}
- Confirm the follow behaviour:
sc.read_file("/work/link-out.txt") # b'adc-sandbox\n' — the contents of /etc/hostname
list_files returns the same FileInfo shape and so loses the same fields.
Expected behavior
FileInfo exposes what the service sends:
- an
is_symlink: bool and a symlink_target: str | None, so a caller can identify and refuse a symlink;
is_directory reflecting the wire's isDir;
mode typed as int | None, matching what is sent — or documented as permission bits only, since it carries no file-type bits and cannot be used for type discrimination.
Additional context
Environment: swedencentral, api-version as shipped in 0.1.0b4, sandbox created from the python-3.13 public disk-image preset. Reproduced against a live sandbox group, not a mock.
The workaround available today is to bypass FileInfo and read the raw stat payload, which means depending on wire shape the typed surface is supposed to insulate callers from.
Describe the bug
FileInfoloses type information that the service actually returns. The data-planeGET .../files/statresponse carriesisSymlinkandsymlinkTarget, andFileInfohas no field for either — so a caller using the typed surface cannot tell a symlink from a regular file.isDiris also mis-mapped, andmodeis annotated asstrbut arrives as anint.Three defects, in one model:
FileInfoisSymlinktruefor a symlinksymlinkTarget/etc/hostnameisDirtruefor a directoryis_directoryisFalsemode420/511/493(int)str | None, populated with the intThis matters beyond tidiness because
read_filefollows symlinks. Reading a path that happens to be a symlink returns the target's contents, from anywhere in the guest filesystem. A caller that wants to refuse symlinked paths has the information available on the wire and no supported way to reach it —modeis permission bits only (0o644/0o777/0o755, noS_IFLNK), andis_directorycannot be trusted.To Reproduce
/work/subis a directory and reportsis_directory=False; the symlink is indistinguishable from the regular file; everymodeis anintdespite thestr | Noneannotation.{"name":"real.txt", "size":12, "mode":420, "isDir":false, "isSymlink":false, "modifiedTime":1786404028} {"name":"link-out.txt", "size":13, "mode":511, "isDir":false, "isSymlink":true, "symlinkTarget":"/etc/hostname", "modifiedTime":1786404028} {"name":"sub", "size":4096, "mode":493, "isDir":true, "isSymlink":false, "modifiedTime":1786404028}list_filesreturns the sameFileInfoshape and so loses the same fields.Expected behavior
FileInfoexposes what the service sends:is_symlink: booland asymlink_target: str | None, so a caller can identify and refuse a symlink;is_directoryreflecting the wire'sisDir;modetyped asint | None, matching what is sent — or documented as permission bits only, since it carries no file-type bits and cannot be used for type discrimination.Additional context
Environment:
swedencentral, api-version as shipped in 0.1.0b4, sandbox created from thepython-3.13public disk-image preset. Reproduced against a live sandbox group, not a mock.The workaround available today is to bypass
FileInfoand read the raw stat payload, which means depending on wire shape the typed surface is supposed to insulate callers from.