Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .claude/skills/convert-to-company-docs/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,32 @@
sys.exit(1)


# M-05: Allowed directories for file access (path restriction)
ALLOWED_UPLOAD_DIRS = [
'knowledge/',
'docs/',
'agents/',
'core/',
'logs/',
]

PROJECT_ROOT = Path(os.environ.get('CLAUDE_PROJECT_DIR', '.')).resolve()


def _is_path_allowed(file_path: str) -> bool:
"""
M-05: Validate that file path is within allowed directories.
Prevents converting arbitrary files (e.g., .env, credentials) to Google Docs.
"""
try:
resolved = Path(file_path).resolve()
rel = resolved.relative_to(PROJECT_ROOT)
rel_str = str(rel).replace('\\', '/')
return any(rel_str.startswith(prefix) for prefix in ALLOWED_UPLOAD_DIRS)
except (ValueError, RuntimeError):
return False


# Cores Company (RGB normalizado 0-1)
COLORS = {
'background': {'red': 0.953, 'green': 0.953, 'blue': 0.953}, # #F3F3F3
Expand Down Expand Up @@ -314,6 +340,12 @@ def convert(self, md_path: str, folder_id: str = None) -> dict:
print(f"ERRO: Arquivo nao encontrado: {md_path}")
return None

# M-05: Validate file path is within allowed directories
if not _is_path_allowed(md_path):
print(f"ERRO [SECURITY]: Conversao bloqueada — path fora dos diretorios permitidos: {md_path}")
print(f" Diretorios permitidos: {ALLOWED_UPLOAD_DIRS}")
return None

# Autenticar
if not self.docs_service:
if not self.authenticate():
Expand Down
35 changes: 35 additions & 0 deletions .claude/skills/sync-docs/gdrive_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,35 @@
sys.exit(1)


# M-05: Allowed directories for upload (path restriction)
ALLOWED_UPLOAD_DIRS = [
'knowledge/',
'docs/',
'agents/',
'core/',
'logs/',
]

PROJECT_ROOT = Path(os.environ.get('CLAUDE_PROJECT_DIR', '.')).resolve()


def _is_upload_path_allowed(local_path: str) -> bool:
"""
M-05: Validate that upload path is within allowed directories.
Prevents uploading arbitrary files (e.g., .env, credentials) to Google Drive.
"""
try:
resolved = Path(local_path).resolve()
# Must be under project root
rel = resolved.relative_to(PROJECT_ROOT)
rel_str = str(rel).replace('\\', '/')
# Must start with one of the allowed prefixes
return any(rel_str.startswith(prefix) for prefix in ALLOWED_UPLOAD_DIRS)
except (ValueError, RuntimeError):
# relative_to raises ValueError if not relative
return False


class GDriveSync:
"""Sincronizador de arquivos com Google Drive usando OAuth."""

Expand Down Expand Up @@ -204,6 +233,12 @@ def upload_file(self, local_path: str, folder_id: str = None, folder_name: str =
print(f"ERRO: Arquivo nao encontrado: {local_path}")
return None

# M-05: Validate upload path is within allowed directories
if not _is_upload_path_allowed(local_path):
print(f"ERRO [SECURITY]: Upload bloqueado — path fora dos diretorios permitidos: {local_path}")
print(f" Diretorios permitidos: {ALLOWED_UPLOAD_DIRS}")
return None

# Determina pasta destino
target_folder_id = folder_id
if not target_folder_id:
Expand Down
6 changes: 3 additions & 3 deletions .claude/skills/sync-docs/reauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
'https://www.googleapis.com/auth/drive.file'
]

# Paths
OAUTH_KEYS = Path(r"~/.config/mcp-gdrive/gcp-oauth.keys.json")
TOKEN_FILE = Path(r"~/.config/mcp-gdrive/.gdrive-server-credentials.json")
# Paths — use Path.home() for cross-platform ~ expansion (M-06 fix)
OAUTH_KEYS = Path.home() / ".config" / "mcp-gdrive" / "gcp-oauth.keys.json"
TOKEN_FILE = Path.home() / ".config" / "mcp-gdrive" / ".gdrive-server-credentials.json"


def main():
Expand Down
Loading