Course outline

Linux Security / Basic: identity, resources, and evidence / Lesson 4

Review paths, links, and resource identity

Distinguish lexical path text from kernel resolution, object identity, and trusted directory boundaries.

About 4 min readContent reviewed
On this page

Lesson goals

  • Explain why string-prefix checks do not establish filesystem containment.
  • Preserve link and changing-path assumptions without accessing the host filesystem.

Treat a pathname as a lookup request

A pathname is interpreted from a starting directory or root through components under the processโ€™s context. Relative paths, mount namespaces, symbolic links, directory permissions, and concurrent changes can alter which object is reached. A string that looks internal is not proof of a trusted object. Use the Linux path-resolution model to ask which directory is trusted and what can change during lookup. This lesson uses invented path strings only. It does not resolve, stat, open, create, remove, or modify a real path, link, mount, or device on this host.

Compare components without claiming containment

A raw prefix test can confuse /lab/reports-old with /lab/reports. The first program demonstrates that mistake and checks component equality for one fixed path using PurePosixPath, which does not access the filesystem. This is a lexical illustration, not a security validator. It does not handle actual symlinks, mounts, races, or all accepted input representations. Even a component match says only that the supplied string has that shape. A production design needs a reviewed directory-relative interface and resolution policy supported by the actual platform, with its assumptions verified separately.

python Inspect fixed path strings without resolving them
from pathlib import PurePosixPath
base = PurePosixPath("/lab/reports")
candidate = PurePosixPath("/lab/reports-old/out.txt")
assert str(candidate).startswith(str(base)) is True
assert candidate.parts[:len(base.parts)] != base.parts
inside_text = PurePosixPath("/lab/reports/out.txt")
assert inside_text.parts[:len(base.parts)] == base.parts
print("raw prefix and lexical components answer different questions")
Expected output
raw prefix and lexical components answer different questions

The complete program performs PurePosixPath string operations only. No filesystem lookup occurs. The final lexical match is not proof that a real resolved object stays inside a directory or has the required ownership and permissions.

The second program models one declared alias from a path that looks inside the report tree to a different fixture object. It is a dictionary lookup, not a symlink resolver. It makes the trust question visible: who can control a component or target between validation and use? A path normalization helper cannot establish those facts. Hard links, symbolic links, bind mounts, and namespace views have different semantics and must not be merged into one generic redirect idea. Record the exact mechanism relevant to a real design before choosing a defense.

python Model an alias using inert object labels
aliases = {"/lab/reports/current":"object-outside-report-scope"}
requested = "/lab/reports/current"
assert requested.startswith("/lab/reports/")
reached = aliases[requested]
assert reached == "object-outside-report-scope"
assert reached != "approved-report-object"
print("inside-looking text does not establish modeled object identity")
Expected output
inside-looking text does not establish modeled object identity

The complete program uses invented strings and a supplied mapping. It creates no link and follows no real path. The result establishes a limitation of the text claim, not an actual filesystem escape or vulnerability in a product.

Keep trusted directory authority narrow

A file-handling design should identify which directory reference is trusted, who can mutate the namespace, and which resolution features are permitted. Linux provides directory-relative interfaces and, on supporting kernels, more specific resolution controls; support and exact semantics must be checked for the selected environment. Avoid teaching a universal sequence of precheck then open as race-safe. A successful check of one path can become stale before a later lookup. Use documented platform primitives in a reviewed implementation, not a homemade string sanitizer. Actual resource access and syscall behavior remain unexecuted in this course.

Verify object-level outcomes in an authorized environment

Test fixed legitimate names, sibling prefixes, parent components, unsupported forms, aliases, and controlled namespace changes in the appropriate approved fixture. Verify the actual object and unchanged protected state on refusal, not only the accepted path string. Keep ownership, permission, and mandatory-policy claims separate from lexical validation. These local examples establish only text and declared-mapping properties. They do not certify containment or harden a real application. A useful conclusion names exactly what the parser or model checked and which kernel-resolution and concurrency boundaries still need evidence.

Common errors and fixes

A startswith check proves containment

Why it happens
Text similarity is confused with component and object resolution.
How to fix it
Use a reviewed platform resolution design and verify actual outcomes.
Prevent it next time
Keep lexical checks explicitly limited.

Normalization proves a path cannot change

Why it happens
Namespace authority and timing are ignored.
How to fix it
Review links, writable components, and atomic resource access.
Prevent it next time
Test controlled check/use changes only in approved fixtures.

A dictionary alias demo is called a real escape test

Why it happens
The model is mistaken for kernel execution.
How to fix it
State the supplied mapping and unexecuted platform boundary.
Prevent it next time
Record actual versus illustrative evidence separately.

Check your understanding

1. Why does /lab/reports-old pass a raw /lab/reports prefix check?

Show worked solution

The character prefix does not include a component boundary. The fixed lexical example detects the sibling difference, but even correct component comparison cannot establish actual filesystem containment because resolution, links, mounts, and races remain outside the string model.

2. A path was checked before it was opened. What remains uncertain?

Show worked solution

Whether the later operation reached the same intended object under the same trusted namespace and credential context. A mutable component or alias can invalidate the earlier observation. Do not perform host experiments here; identify a supported atomic or constrained platform design and plan separate authorized verification.

Key takeaways

  • A path string is not a complete resource identity.
  • Lexical comparison does not prove kernel containment.
  • Namespace control and check/use timing need explicit design.

Official references