T09 · Insecure Skill Coding Practices
- Location
scripts/crypto.sh:176- Finding
Portfolio Holdings Stored Without Restrictive File Permissions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/crypto.sh:176-182, 197-199
Vulnerability Type: Plaintext sensitive financial data with permissions inherited from the process umask
Risk Level: MediumVulnerable Code
python DATA_DIR = os.path.expanduser("~/.crypto-tracker") PF = os.path.join(DATA_DIR, "portfolio.json") if not os.path.isdir(DATA_DIR): os.makedirs(DATA_DIR) if not os.path.isfile(PF): with open(PF, "w") as f: json.dump([], f) portfolio.append({"coin": coin, "amount": amt, "buy_price": price}) with open(PF, "w") as f: json.dump(portfolio, f, indent=2)Technical Analysis
The portfolio feature stores coin identifiers, holding amounts, and purchase prices in plaintext at
~/.crypto-tracker/portfolio.json. Neither the directory nor the file is created with explicit restrictive permissions.os.makedirs(DATA_DIR)andopen(PF, "w")inherit permissions from the process umask. Under a common umask of0022, the directory can be created as mode0755and the file as mode0644. On a multi-user system, this can allow other local users to traverse the directory and read the portfolio.The application does not need to expose this information to other users. Therefore, broadly readable storage exceeds the minimum access required by the portfolio-tracking functionality.
No evidence was found that portfolio amounts or purchase prices are transmitted to CoinGecko or another remote service. The network request used by the portfolio listing feature sends only coin identifiers.
Attack Path
- A victim invokes
crypto.sh portfolio add, causing the script to create~/.crypto-tracker/portfolio.json. - The file receives permissions determined by the victim's current umask.
- If the resulting permissions permit access by other users, an attacker with a separate local account identifies the victim's home directory.
- The attacker reads `~/.crypto-tracker/portfolio.j ...[truncated 805 chars]
- A victim invokes
- Remediation
View remediation
Remediation Suggestions
-
Set a restrictive umask before creating any portfolio data:
bash umask 077 -
Create the data directory with mode
0700:python os.makedirs(DATA_DIR, mode=0o700, exist_ok=True) os.chmod(DATA_DIR, 0o700) -
Create and maintain the portfolio file with mode
0600:python import os fd = os.open(PF, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) with os.fdopen(fd, "w") as f: json.dump(portfolio, f, indent=2) os.chmod(PF, 0o600) -
Correct permissions on existing installations, because secure creation logic will not automatically repair an already permissive file.
-
Use atomic writes through a user-private temporary file, followed by
os.replace, to avoid partial or corrupted portfolio data. -
Clearly document that the portfolio is stored locally in plaintext. If confidentiality against disk access is required, provide an optional encrypted storage mechanism backed by an operating-system credential store or a user-supplied encryption key.
-
