Back to skill

Security audit

Umask Tool

Security checks for vulnerabilities and agentic risk

Overview

This skill is a small umask utility, but its script silently sets the process umask to 000 instead of safely showing or setting the requested mask.

Review before installing. A umask tool is inherently about file permissions, but this implementation should restore the old mask when displaying it, require an explicit validated mask before changing it, and avoid top-level import side effects.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/umask.py:1
Finding

Display Operation Silently Sets the Process Umask to 000

Content
View full analysis

Vulnerability Details

File Location: scripts/umask.py, lines 1–3
Vulnerability Type: Unintended security-sensitive state modification
Risk Level: Medium

Vulnerable Code

python
#!/usr/bin/env python3
import os
print(oct(os.umask(0)))

Technical Analysis

The call os.umask(0) returns the previous file-creation mask but also changes the current process mask to 000. Consequently, the operation documented as displaying the current mask performs an undocumented security-sensitive mutation.

When the script is launched as a separate process, this change ordinarily ends when that child process exits and cannot modify the parent shell's umask. However, if the file is imported, evaluated with exec, or otherwise run inside a long-lived Python process, its top-level code changes that host process's umask to 000. Files subsequently created with a requested mode of 0666 may therefore be readable and writable by other local users, while directories requested with mode 0777 may be fully accessible to them.

The implementation also does not parse the documented optional mask argument and lacks a __main__ guard, causing the mutation at import time.

Attack Path

  1. A long-lived Python application imports or evaluates scripts/umask.py in its own process.
  2. The top-level call to os.umask(0) replaces the application's existing restrictive mask with 000.
  3. The application later creates files or directories without explicitly removing group or world permissions.
  4. Those objects receive permissions permitted by their requested creation modes, potentially up to 0666 for files and 0777 for directories.
  5. Another local user reads, modifies, or traverses the exposed objects where operating-system and filesystem controls permit it.

This path does not apply in the same way when the utility is executed solely as a short-lived subprocess, because a child process cannot change its parent's umask.

Impact Assessment

The f ...[truncated 572 chars]

Remediation
View remediation

Remediation Suggestions

Read the current mask by changing it only temporarily and restoring it immediately:

python
def get_umask():
    current = os.umask(0)
    os.umask(current)
    return current

Prevent import-time side effects and implement the documented command-line behavior explicitly:

python
#!/usr/bin/env python3
import argparse
import os


def get_umask():
    current = os.umask(0)
    os.umask(current)
    return current


def parse_mask(value):
    try:
        mask = int(value, 8)
    except ValueError as exc:
        raise argparse.ArgumentTypeError("mask must be an octal value") from exc

    if not 0 <= mask <= 0o777:
        raise argparse.ArgumentTypeError("mask must be between 000 and 777")
    return mask


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("mask", nargs="?", type=parse_mask)
    args = parser.parse_args()

    if args.mask is None:
        print(f"{get_umask():03o}")
    else:
        os.umask(args.mask)


if __name__ == "__main__":
    main()

Additional hardening measures:

  • Add tests confirming that display-only execution restores the original mask.
  • Add an import test confirming that importing the module has no side effects.
  • Validate masks strictly as octal values in the range 000 through 777.
  • Document that a standalone child process cannot persistently change its parent shell's umask. If persistent shell modification is required, provide an explicit shell integration mechanism rather than implying that a subprocess can change its caller's state.
  • Prefer restrictive defaults such as 077 when the utility creates sensitive files or directories.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description says the skill can set or display the file creation permission mask. However, this code does not merely display the current mask: in Python, os.umask(0) both returns the previous umask and changes the current process umask to 0. That means the script actively modifies permissions, making subsequently created files/directories maximally permissive relative to defaults. It also does not accept input or implement a general 'set' operation. Because the actual behavior is an unconditional permission-changing side effect not clearly represented as such, this is a material mismatch.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
100% confidence
Finding

Resetting the umask to 0 makes subsequently created files and directories inherit maximally open default permissions, subject only to application mode bits. In a skill whose stated purpose includes display and safe permission control, doing this unconditionally is dangerous because it can expose sensitive files to other local users and weaken downstream security assumptions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script claims to display the current umask, but it does so by calling os.umask(0), which both returns the old mask and permanently changes the process umask to 0. That means any files or directories created later in the same process may become far more permissive than intended, creating an unexpected privilege and data exposure risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script silently changes process state without informing the user, even though umask directly affects security-sensitive default permissions for future file creation. This lack of warning is especially risky in an agent skill context, where callers may expect a read-only status action but instead receive a persistent side effect during the process lifetime.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.