Bug Description
The entire ~/.iac-code/ configuration directory — including credential files, session history, logs, and tool results — is created with default file permissions (0644 for files, 0755 for directories), making them readable by all users on the system. These files may contain API keys, conversation history with sensitive information, and cloud credentials.
Affected Files and Directories
| Path |
Current |
Expected |
Contains |
~/.iac-code/ |
0755 |
0700 |
All config |
~/.iac-code/.credentials.yml |
0644 |
0600 |
API keys |
~/.iac-code/.cloud-credentials.yml |
0644 |
0600 |
Cloud AK/SK |
~/.iac-code/projects/**/*.jsonl |
0644 |
0600 |
Session history |
~/.iac-code/logs/*.log |
0644 |
0600 |
Debug logs |
~/.iac-code/tool-results/ |
0755 |
0700 |
Tool execution output |
Steps to Reproduce
- Run
iac-code and configure credentials via /auth
- Check permissions:
ls -la ~/.iac-code/.credentials.yml
# -rw-r--r-- 1 user staff ... .credentials.yml
stat -c '%a' ~/.iac-code/
# 755
find ~/.iac-code/ -name "*.jsonl" -perm /044 | wc -l
# All session files are world-readable
Expected Behavior
- Config directory:
0700 (owner access only)
- Credential files:
0600 (owner read/write only)
- Session files:
0600 (may contain sensitive conversation content)
- Log files:
0600 (may contain error messages with credentials)
Actual Behavior
All files and directories use default umask permissions (0644/0755), readable by any user on the system.
Root Cause
-
Credential files: _save_yaml in src/iac_code/config.py uses path.write_text() without restricting permissions:
def _save_yaml(path: Path, data: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(yaml.dump(data, ...), encoding="utf-8")
-
Config directory: get_config_dir() creates the directory with default permissions:
config_dir.mkdir(parents=True, exist_ok=True) # inherits umask (typically 022)
-
Session and log files: Written via standard open() / json.dumps() without explicit permission setting.
Suggested Fix
The codebase already has restrict_file_permissions() in src/iac_code/utils/file_security.py (used by the A2A push subsystem), but it is NOT applied to the main config directory or credentials:
from iac_code.utils.file_security import restrict_file_permissions
# Fix 1: Restrict config directory on creation
def get_config_dir() -> Path:
config_dir = _resolve_config_dir()
created = not config_dir.exists()
config_dir.mkdir(parents=True, exist_ok=True)
if created:
restrict_file_permissions(config_dir, directory=True)
return config_dir
# Fix 2: Restrict credential files after write
def _save_yaml(path: Path, data: dict[str, Any], *, restricted: bool = False) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(yaml.dump(data, ...), encoding="utf-8")
if restricted:
restrict_file_permissions(path, directory=False)
Security Impact
- Attack vector: Any local user on the same system can read API keys, cloud credentials, and session history
- Affected data: API keys, AccessKey/SecretKey pairs, conversation content, debug logs
- Severity: Medium on multi-user systems (servers, shared workstations), Low on personal machines
- Note: Tools like
ssh, gpg, and aws-cli refuse to use or create key files with overly permissive permissions for this reason
Operating System
macOS
Python Version
3.12.7
iac-code Version
0.2.3
Additional Context
- The
restrict_file_permissions() utility already exists and is used by a2a/push_queue.py and a2a/push_secrets.py, but not by the core config/credential/session writing paths.
- The
keyring package is listed as a dependency but only used by qwenpaw_source.py, not for main credential storage.
- Session files (
.jsonl) may contain tool outputs that reference internal systems, file contents, or cloud resource details discussed in conversations.
Bug Description
The entire
~/.iac-code/configuration directory — including credential files, session history, logs, and tool results — is created with default file permissions (0644for files,0755for directories), making them readable by all users on the system. These files may contain API keys, conversation history with sensitive information, and cloud credentials.Affected Files and Directories
~/.iac-code/07550700~/.iac-code/.credentials.yml06440600~/.iac-code/.cloud-credentials.yml06440600~/.iac-code/projects/**/*.jsonl06440600~/.iac-code/logs/*.log06440600~/.iac-code/tool-results/07550700Steps to Reproduce
iac-codeand configure credentials via/authExpected Behavior
0700(owner access only)0600(owner read/write only)0600(may contain sensitive conversation content)0600(may contain error messages with credentials)Actual Behavior
All files and directories use default umask permissions (
0644/0755), readable by any user on the system.Root Cause
Credential files:
_save_yamlinsrc/iac_code/config.pyusespath.write_text()without restricting permissions:Config directory:
get_config_dir()creates the directory with default permissions:Session and log files: Written via standard
open()/json.dumps()without explicit permission setting.Suggested Fix
The codebase already has
restrict_file_permissions()insrc/iac_code/utils/file_security.py(used by the A2A push subsystem), but it is NOT applied to the main config directory or credentials:Security Impact
ssh,gpg, andaws-clirefuse to use or create key files with overly permissive permissions for this reasonOperating System
macOS
Python Version
3.12.7
iac-code Version
0.2.3
Additional Context
restrict_file_permissions()utility already exists and is used bya2a/push_queue.pyanda2a/push_secrets.py, but not by the core config/credential/session writing paths.keyringpackage is listed as a dependency but only used byqwenpaw_source.py, not for main credential storage..jsonl) may contain tool outputs that reference internal systems, file contents, or cloud resource details discussed in conversations.