Skip to content

Commit cc809c5

Browse files
authored
Merge pull request #21 from laurigates/pr-3-security-input-validation
feat: add comprehensive security and input validation system
2 parents 9dbea13 + d8e2f73 commit cc809c5

File tree

17 files changed

+3242
-75
lines changed

17 files changed

+3242
-75
lines changed

.github/workflows/ci.yml

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ jobs:
3939
fail-fast: false
4040
matrix:
4141
os: [ubuntu-latest, macos-latest]
42-
python-version: ["3.10", "3.11", "3.12"]
42+
python-version: ["3.10", "3.11", "3.12", "3.13"]
43+
exclude:
44+
- os: macos-latest
45+
python-version: "3.10"
4346

4447
name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }}
4548

@@ -69,6 +72,29 @@ jobs:
6972
file: ./coverage.xml
7073
fail_ci_if_error: false
7174

75+
security:
76+
runs-on: ubuntu-latest
77+
name: Security Scan
78+
79+
steps:
80+
- uses: actions/checkout@v4
81+
82+
- name: Install uv
83+
uses: astral-sh/setup-uv@v4
84+
with:
85+
enable-cache: true
86+
87+
- name: Set up Python 3.12
88+
run: |
89+
uv python install 3.12
90+
uv python pin 3.12
91+
92+
- name: Install dependencies
93+
run: uv sync --group dev
94+
95+
- name: Run security scan
96+
run: uv run bandit -r kicad_mcp/
97+
7298
build:
7399
runs-on: ubuntu-latest
74100
name: Build Package

Makefile

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,27 @@
22

33
help:
44
@echo "Available commands:"
5-
@echo " install Install dependencies"
6-
@echo " test Run tests"
7-
@echo " lint Run linting"
8-
@echo " format Format code"
9-
@echo " clean Clean build artifacts"
10-
@echo " build Build package"
5+
@echo " install Install dependencies"
6+
@echo " test Run tests"
7+
@echo " test <file> Run specific test file"
8+
@echo " lint Run linting"
9+
@echo " format Format code"
10+
@echo " clean Clean build artifacts"
11+
@echo " build Build package"
12+
@echo " run Start the KiCad MCP server"
1113

1214
install:
1315
uv sync --group dev
1416

1517
test:
16-
uv run python -m pytest tests/ -v
18+
# Collect extra args; if none, use tests/
19+
@files="$(filter-out $@,$(MAKECMDGOALS))"; \
20+
if [ -z "$$files" ]; then files="tests/"; fi; \
21+
uv run pytest $$files -v
22+
23+
# Prevent “No rule to make target …” errors for the extra args
24+
%::
25+
@:
1726

1827
lint:
1928
uv run ruff check kicad_mcp/ tests/

kicad_mcp/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""
2-
KiCad MCP Server - A Model Context Protocol server for KiCad.
2+
KiCad MCP Server.
3+
4+
A Model Context Protocol (MCP) server for KiCad electronic design automation (EDA) files.
35
"""
46
from .server import *
57
from .config import *
@@ -24,4 +26,4 @@
2426
# Lifespan / context helpers
2527
"kicad_lifespan",
2628
"KiCadAppContext",
27-
]
29+
]

kicad_mcp/config.py

Lines changed: 144 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,46 @@
11
"""
22
Configuration settings for the KiCad MCP server.
3+
4+
This module provides platform-specific configuration for KiCad integration,
5+
including file paths, extensions, component libraries, and operational constants.
6+
All settings are determined at import time based on the operating system.
7+
8+
Module Variables:
9+
system (str): Operating system name from platform.system()
10+
KICAD_USER_DIR (str): User's KiCad documents directory
11+
KICAD_APP_PATH (str): KiCad application installation path
12+
ADDITIONAL_SEARCH_PATHS (List[str]): Additional project search locations
13+
DEFAULT_PROJECT_LOCATIONS (List[str]): Common project directory patterns
14+
KICAD_PYTHON_BASE (str): KiCad Python framework base path (macOS only)
15+
KICAD_EXTENSIONS (Dict[str, str]): KiCad file extension mappings
16+
DATA_EXTENSIONS (List[str]): Recognized data file extensions
17+
CIRCUIT_DEFAULTS (Dict[str, Union[float, List[float]]]): Default circuit parameters
18+
COMMON_LIBRARIES (Dict[str, Dict[str, Dict[str, str]]]): Component library mappings
19+
DEFAULT_FOOTPRINTS (Dict[str, List[str]]): Default footprint suggestions per component
20+
TIMEOUT_CONSTANTS (Dict[str, float]): Operation timeout values in seconds
21+
PROGRESS_CONSTANTS (Dict[str, int]): Progress reporting percentage values
22+
DISPLAY_CONSTANTS (Dict[str, int]): UI display configuration values
23+
24+
Platform Support:
25+
- macOS (Darwin): Full support with application bundle paths
26+
- Windows: Standard installation paths
27+
- Linux: System package paths
28+
- Unknown: Defaults to macOS paths for compatibility
29+
30+
Dependencies:
31+
- os: File system operations and environment variables
32+
- platform: Operating system detection
333
"""
4-
import os
534

35+
import os
636
import platform
737

8-
# Determine operating system
38+
# Determine operating system for platform-specific configuration
39+
# Returns 'Darwin' (macOS), 'Windows', 'Linux', or other
940
system = platform.system()
1041

11-
# KiCad paths based on operating system
42+
# Platform-specific KiCad installation and user directory paths
43+
# These paths are used for finding KiCad resources and user projects
1244
if system == "Darwin": # macOS
1345
KICAD_USER_DIR = os.path.expanduser("~/Documents/KiCad")
1446
KICAD_APP_PATH = "/Applications/KiCad/KiCad.app"
@@ -19,42 +51,52 @@
1951
KICAD_USER_DIR = os.path.expanduser("~/KiCad")
2052
KICAD_APP_PATH = "/usr/share/kicad"
2153
else:
22-
# Default to macOS paths if system is unknown
54+
# Default to macOS paths if system is unknown for maximum compatibility
55+
# This ensures the server can start even on unrecognized platforms
2356
KICAD_USER_DIR = os.path.expanduser("~/Documents/KiCad")
2457
KICAD_APP_PATH = "/Applications/KiCad/KiCad.app"
2558

26-
# Additional search paths from environment variable
59+
# Additional search paths from environment variable KICAD_SEARCH_PATHS
60+
# Users can specify custom project locations as comma-separated paths
2761
ADDITIONAL_SEARCH_PATHS = []
2862
env_search_paths = os.environ.get("KICAD_SEARCH_PATHS", "")
2963
if env_search_paths:
3064
for path in env_search_paths.split(","):
31-
expanded_path = os.path.expanduser(path.strip())
32-
if os.path.exists(expanded_path):
65+
expanded_path = os.path.expanduser(path.strip()) # Expand ~ and variables
66+
if os.path.exists(expanded_path): # Only add existing directories
3367
ADDITIONAL_SEARCH_PATHS.append(expanded_path)
3468

35-
# Try to auto-detect common project locations if not specified
69+
# Auto-detect common project locations for convenient project discovery
70+
# These are typical directory names users create for electronics projects
3671
DEFAULT_PROJECT_LOCATIONS = [
37-
"~/Documents/PCB",
38-
"~/PCB",
39-
"~/Electronics",
40-
"~/Projects/Electronics",
41-
"~/Projects/PCB",
42-
"~/Projects/KiCad"
72+
"~/Documents/PCB", # Common Windows/macOS location
73+
"~/PCB", # Simple home directory structure
74+
"~/Electronics", # Generic electronics projects
75+
"~/Projects/Electronics", # Organized project structure
76+
"~/Projects/PCB", # PCB-specific project directory
77+
"~/Projects/KiCad", # KiCad-specific project directory
4378
]
4479

80+
# Add existing default locations to search paths
81+
# Avoids duplicates and only includes directories that actually exist
4582
for location in DEFAULT_PROJECT_LOCATIONS:
4683
expanded_path = os.path.expanduser(location)
4784
if os.path.exists(expanded_path) and expanded_path not in ADDITIONAL_SEARCH_PATHS:
4885
ADDITIONAL_SEARCH_PATHS.append(expanded_path)
4986

50-
# Base path to KiCad's Python framework
87+
# Base path to KiCad's Python framework for API access
88+
# macOS bundles Python framework within the application
5189
if system == "Darwin": # macOS
52-
KICAD_PYTHON_BASE = os.path.join(KICAD_APP_PATH, "Contents/Frameworks/Python.framework/Versions")
90+
KICAD_PYTHON_BASE = os.path.join(
91+
KICAD_APP_PATH, "Contents/Frameworks/Python.framework/Versions"
92+
)
5393
else:
94+
# Linux/Windows use system Python or require dynamic detection
5495
KICAD_PYTHON_BASE = "" # Will be determined dynamically in python_path.py
5596

5697

57-
# File extensions
98+
# KiCad file extension mappings for project file identification
99+
# Used by file discovery and validation functions
58100
KICAD_EXTENSIONS = {
59101
"project": ".kicad_pro",
60102
"pcb": ".kicad_pcb",
@@ -66,8 +108,92 @@
66108
"kibot_config": ".kibot.yaml",
67109
}
68110

69-
# Recognized data files
111+
# Additional data file extensions that may be part of KiCad projects
112+
# Includes manufacturing files, component data, and export formats
70113
DATA_EXTENSIONS = [
71114
".csv", # BOM or other data
72115
".pos", # Component position file
116+
".net", # Netlist files
117+
".zip", # Gerber files and other archives
118+
".drl", # Drill files
73119
]
120+
121+
# Default parameters for circuit creation and component placement
122+
# Values in mm unless otherwise specified, following KiCad conventions
123+
CIRCUIT_DEFAULTS = {
124+
"grid_spacing": 1.0, # Default grid spacing in mm for user coordinates
125+
"component_spacing": 10.16, # Default component spacing in mm
126+
"wire_width": 6, # Default wire width in KiCad units (0.006 inch)
127+
"text_size": [1.27, 1.27], # Default text size in mm
128+
"pin_length": 2.54, # Default pin length in mm
129+
}
130+
131+
# Predefined component library mappings for quick circuit creation
132+
# Maps common component types to their KiCad library and symbol names
133+
# Organized by functional categories: basic, power, connectors
134+
COMMON_LIBRARIES = {
135+
"basic": {
136+
"resistor": {"library": "Device", "symbol": "R"},
137+
"capacitor": {"library": "Device", "symbol": "C"},
138+
"inductor": {"library": "Device", "symbol": "L"},
139+
"led": {"library": "Device", "symbol": "LED"},
140+
"diode": {"library": "Device", "symbol": "D"},
141+
},
142+
"power": {
143+
"vcc": {"library": "power", "symbol": "VCC"},
144+
"gnd": {"library": "power", "symbol": "GND"},
145+
"+5v": {"library": "power", "symbol": "+5V"},
146+
"+3v3": {"library": "power", "symbol": "+3V3"},
147+
"+12v": {"library": "power", "symbol": "+12V"},
148+
"-12v": {"library": "power", "symbol": "-12V"},
149+
},
150+
"connectors": {
151+
"conn_2pin": {"library": "Connector", "symbol": "Conn_01x02_Male"},
152+
"conn_4pin": {"library": "Connector_Generic", "symbol": "Conn_01x04"},
153+
"conn_8pin": {"library": "Connector_Generic", "symbol": "Conn_01x08"},
154+
},
155+
}
156+
157+
# Suggested footprints for common components, ordered by preference
158+
# SMD variants listed first, followed by through-hole alternatives
159+
DEFAULT_FOOTPRINTS = {
160+
"R": [
161+
"Resistor_SMD:R_0805_2012Metric",
162+
"Resistor_SMD:R_0603_1608Metric",
163+
"Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal",
164+
],
165+
"C": [
166+
"Capacitor_SMD:C_0805_2012Metric",
167+
"Capacitor_SMD:C_0603_1608Metric",
168+
"Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P5.00mm",
169+
],
170+
"LED": ["LED_SMD:LED_0805_2012Metric", "LED_THT:LED_D5.0mm"],
171+
"D": ["Diode_SMD:D_SOD-123", "Diode_THT:D_DO-35_SOD27_P7.62mm_Horizontal"],
172+
}
173+
174+
# Operation timeout values in seconds for external process management
175+
# Prevents hanging operations and provides user feedback
176+
TIMEOUT_CONSTANTS = {
177+
"kicad_cli_version_check": 10.0, # Timeout for KiCad CLI version checks
178+
"kicad_cli_export": 30.0, # Timeout for KiCad CLI export operations
179+
"application_open": 10.0, # Timeout for opening applications (e.g., KiCad)
180+
"subprocess_default": 30.0, # Default timeout for subprocess operations
181+
}
182+
183+
# Progress percentage milestones for long-running operations
184+
# Provides consistent progress reporting across different tools
185+
PROGRESS_CONSTANTS = {
186+
"start": 10, # Initial progress percentage
187+
"detection": 20, # Progress after CLI detection
188+
"setup": 30, # Progress after setup complete
189+
"processing": 50, # Progress during processing
190+
"finishing": 70, # Progress when finishing up
191+
"validation": 90, # Progress during validation
192+
"complete": 100, # Progress when complete
193+
}
194+
195+
# User interface display configuration values
196+
# Controls how much information is shown in previews and summaries
197+
DISPLAY_CONSTANTS = {
198+
"bom_preview_limit": 20, # Maximum number of BOM items to show in preview
199+
}

kicad_mcp/tools/bom_tools.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,9 @@ def parse_bom_file(file_path: str) -> Tuple[List[Dict[str, Any]], Dict[str, Any]
297297
components.append(dict(row))
298298

299299
elif ext == '.xml':
300-
# Basic XML parsing
301-
import xml.etree.ElementTree as ET
302-
tree = ET.parse(file_path)
300+
# Basic XML parsing with security protection
301+
from defusedxml.ElementTree import parse as safe_parse
302+
tree = safe_parse(file_path)
303303
root = tree.getroot()
304304

305305
format_info["detected_format"] = "xml"

0 commit comments

Comments
 (0)