# Understanding the Symlink Security Check in DCG's System Configuration Loading

> Learn how the DCG symlink security check prevents privilege escalation by verifying system configuration files aren't user writable symlinks before loading.

- Repository: [Jeff Emanuel/destructive_command_guard](https://github.com/Dicklesworthstone/destructive_command_guard)
- Tags: deep-dive
- Published: 2026-07-13

---

**The symlink security check in DCG prevents privilege escalation by verifying that system configuration files are not symlinks pointing to user-writable directories before loading them.**

DCG (destructive_command_guard) implements a layered configuration system that loads settings from environment, project, user, and system sources. When loading the system configuration layer from [`/etc/dcg/config.toml`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main//etc/dcg/config.toml) on Unix or `%ProgramData%\dcg\config.toml` on Windows, the application performs a critical symlink security check to prevent non-privileged users from hijacking administrator settings through symbolic link attacks.

## How the Symlink Security Check Works

The security mechanism operates during the `read_config_file_bounded` function execution, specifically when processing the `ConfigSource::System` layer.

### Bounded File Reading

Before any security checks, DCG enforces a **1 MiB size limit** on configuration files to prevent memory exhaustion attacks. The `read_config_file_bounded` function caps the file size before loading content into memory.

### System Layer Detection

When the configuration source is identified as `ConfigSource::System`, DCG inspects the file metadata without following symbolic links using `fs::symlink_metadata`. This examination occurs in [`src/config.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/config.rs) at lines 45-58:

```rust
if source == ConfigSource::System {
    match fs::symlink_metadata(path) {
        Ok(meta) if meta.file_type().is_symlink() => {
            if symlink_target_is_user_writable(path) {
                eprintln!(
                    "Warning: refusing to load system config '{}' — symlink to user-writable target",
                    path.display()
                );
                return None;
            }
        }
        // … other branches omitted …
    }
}

```

### Symlink Target Validation

If the metadata indicates the path is a symlink, DCG invokes `symlink_target_is_user_writable` (defined at lines 12-33 in [`src/config.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/config.rs)) to validate the target's security posture:

```rust
fn symlink_target_is_user_writable(path: &Path) -> bool {
    let Ok(target) = fs::canonicalize(path) else { return true };
    #[cfg(unix)]
    {
        use std::os::unix::fs::{MetadataExt, PermissionsExt};
        let parent = target.parent().unwrap_or(&target);
        let Ok(meta) = fs::metadata(parent) else { return true };
        meta.uid() != 0 || (meta.permissions().mode() & 0o022) != 0
    }
    #[cfg(not(unix))]
    { false }
}

```

The function resolves the symlink target using `fs::canonicalize`, then examines the parent directory's ownership and permissions. On Unix systems, the target is considered user-writable if the parent directory is **not owned by root (UID 0)** or if it has **group or world-write permissions** (mode bits `0o022`). On non-Unix platforms, the check defaults to allowing the symlink since the attack surface differs.

## Security Implications

The symlink security check serves critical protective functions:

- **Privilege escalation prevention**: System configuration is intended for administrator control. Without this check, a regular user could create a symlink from [`/etc/dcg/config.toml`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main//etc/dcg/config.toml) to a user-controlled file, injecting arbitrary settings into a privileged configuration layer.
- **Fail-safe design**: The check errs conservatively. Any error during target inspection—such as missing permissions or inaccessible paths—results in refusing to load the file, ensuring potentially compromised configurations are skipped.
- **Layered integrity preservation**: Since DCG's configuration layers follow priority rules (environment > project > user > system), refusing a malicious system config preserves the intended security model without silently falling back to compromised settings.

## Practical Examples

### Detecting a Malicious Symlink

Attempting to load a system configuration through a user-writable symlink triggers the security warning:

```bash

# Create a user-writable file and a symlink under /etc/dcg

mkdir -p /tmp/user_config
echo 'check_updates = false' > /tmp/user_config/bad.toml
sudo ln -s /tmp/user_config/bad.toml /etc/dcg/config.toml

# Run dcg (it will emit a warning and skip the system config)

dcg --version

# → Warning: refusing to load system config '/etc/dcg/config.toml' — symlink to user-writable target

```

DCG proceeds with remaining configuration layers (user, project, environment) while skipping the compromised system layer.

### Loading a Valid System Configuration

When the system configuration is a regular file owned by root, loading proceeds normally:

```bash

# Write a proper system config as root

sudo sh -c "echo 'check_updates = true' > /etc/dcg/config.toml"

# dcg now reads the file without warnings

dcg --version

# (no warning, system config applied)

```

### Programmatic Usage

When using DCG's configuration API directly, the symlink check executes automatically:

```rust
use std::path::Path;
use dcg::config::{read_config_file_bounded, ConfigSource};

fn load_system_config() -> Option<String> {
    let sys_path = dcg::config::system_config_dir().join("config.toml");
    read_config_file_bounded(&sys_path, ConfigSource::System)
}

```

The `read_config_file_bounded` function automatically applies the symlink security check when the `ConfigSource::System` variant is passed.

## Key Source Files

The symlink security logic resides in the following locations within the `Dicklesworthstone/destructive_command_guard` repository:

- **[`src/config.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/config.rs) (lines 45-58)**: Contains the conditional guard that detects symlinks in system configuration paths and refuses to load user-writable targets.
- **[`src/config.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/config.rs) (lines 12-33)**: Implements the `symlink_target_is_user_writable` helper function that examines directory ownership and permission bits on Unix systems.

## Summary

- The **symlink security check** in DCG prevents privilege escalation by validating system configuration files before loading.
- Located in [`src/config.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/config.rs), the check uses `fs::symlink_metadata` to detect symlinks without following them, then validates targets using `symlink_target_is_user_writable`.
- On Unix systems, targets are rejected if their parent directories are not owned by root (UID 0) or possess group/world-write permissions (mode `0o022`).
- When a suspicious symlink is detected, DCG emits a warning and returns `None`, causing the system configuration layer to be skipped while preserving higher-priority layers.
- The check defaults to allowing symlinks on non-Unix platforms where the attack surface differs.

## Frequently Asked Questions

### What triggers the symlink security check in DCG?

The check triggers when DCG attempts to load the system configuration layer from [`/etc/dcg/config.toml`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main//etc/dcg/config.toml) (Unix) or `%ProgramData%\dcg\config.toml` (Windows). Specifically, when `read_config_file_bounded` is called with `ConfigSource::System`, it examines the file metadata to determine if the path is a symbolic link before reading contents.

### How does DCG determine if a symlink target is user-writable?

DCG resolves the symlink using `fs::canonicalize`, then examines the parent directory's metadata. On Unix systems, the `symlink_target_is_user_writable` function checks if the parent directory's UID is not 0 (root) or if the permission mode has group or world-write bits set (`0o022`). If either condition is true, the target is considered user-writable. On non-Unix systems, the function returns false, allowing the symlink.

### What happens when DCG detects a suspicious symlink?

When DCG detects a symlink pointing to a user-writable target, it prints a warning message to stderr indicating the refusal to load the system config, then returns `None` from the configuration loader. This causes DCG to skip the system configuration layer entirely and proceed with remaining layers (user, project, environment), maintaining security without crashing the application.

### Does the symlink security check work on Windows?

On Windows and other non-Unix platforms, the `symlink_target_is_user_writable` function immediately returns `false`, effectively allowing symlinks in system configuration paths. This design recognizes that the Unix-specific privilege escalation attack (involving UID and permission bits) does not apply to Windows filesystem semantics, though the bounded file size check still applies universally.