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.