Hardcoded Absolute Path
ID |
csharp.hardcoded_absolute_path |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
CSharp |
Tags |
portability, reliability |
Description
Reports a string literal whose value is an absolute filesystem path, in either the POSIX form
(/etc/myapp/config.json) or a Windows form (C:\ProgramData\MyApp, \\server\share\data).
Verbatim (@"…") and raw literals are decoded and checked like any other; interpolated strings
are not checked, since their value is not known from the source.
Detection deliberately under-reports. A POSIX literal is reported only when its first segment is one
of the standard top-level directories an operating system actually exposes — the configurable
absolutePathRoots list — and only when a further non-empty segment follows it, so a route template such
as /api/users, a bare /etc and a fragment such as /tmp/ are all left alone. The match is
case-sensitive, which keeps /Home/Index apart from /home. Anything containing :// is a URL and
is skipped, as are very short and very long literals. A literal passed as an argument to an
assertion call — Assert.Equal("/usr/share/solr/…", results.indexPath) — is not reported either:
it states an expected value the test compares against, never a path the test itself reads from.
Rationale
An absolute path ties the program to one machine. It is written against the developer’s own layout,
survives review because it is obviously correct there, and then fails everywhere else: the build
agent has no /home/alice, the Linux container has no C:\, the service account cannot read
another user’s profile directory, and a colleague who installed the product elsewhere gets nothing.
The failure also arrives late and far from its cause — at run time, in the deployed environment, as a file-not-found or an access-denied whose message names a directory that appears nowhere in the configuration. A path composed from a configured root fails at start-up instead, with a message that names the setting to change.
public class Report
{
private const string Config = "/etc/myapp/config.json"; // FLAW
public void Write(string root, string name)
{
var log = @"C:\Temp\myapp.log"; // FLAW
var share = @"\\build01\artifacts\drop"; // FLAW
var output = Path.Combine(root, "reports", name); // OK
var route = "/api/users"; // OK, not a filesystem root
var relative = "config/app.json"; // OK
}
}
Remediation
Read the root directory from configuration — IConfiguration, an environment variable, a command
line option — and compose the rest with Path.Combine, which also applies the right separator for
the platform:
var root = configuration["Storage:Root"];
var log = Path.Combine(root, "myapp.log");
For the well-known locations the platform already knows about, ask it rather than spelling the path
out: Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Path.GetTempPath(),
AppContext.BaseDirectory. These resolve correctly on every operating system and for every user.
Configuration
properties:
# Top-level directories that begin a genuine POSIX absolute path (case-sensitive).
absolutePathRoots: [home, etc, var, opt, usr, root, tmp, mnt, srv,
proc, sys, dev, run, boot, sbin, bin, lib, media]
A literal starting with / is reported only when its first segment appears in absolutePathRoots.
Extend the list to cover roots specific to your platforms — Library, Applications and Volumes
on macOS, or a mount point your deployments use — bearing in mind that every added segment is also a
plausible URL route and therefore a source of noise.