--- name: security-scan description: Run security checks from Sept 2026 audit - scan for hardcoded credentials, exposed keys, missing authentication, certificate issues --- # Security Scan Monitor for security regressions based on September 2026 security audit findings (4 critical + 6 high). ## What This Skill Does Automated security checks: 1. Scan for hardcoded credentials and API keys 2. Check for collector keys in committed files 3. Verify certificate validation enabled 4. Check authentication on sensitive endpoints 5. Scan for SQL injection vulnerabilities 6. Verify HTTPS enforcement 7. Check for exposed sensitive data in logs 8. Validate input sanitization ## Background: Sept 2026 Audit **Critical findings addressed:** 1. Unauthenticated inventory updates (FIXED) 2. Deployment credential exposure in registry (MITIGATED) 3. No certificate validation in agent (PLANNED) 4. Hardcoded admin credentials (REMOVED) **This skill prevents regressions** - ensures fixes stay fixed and new code doesn't reintroduce vulnerabilities. ## Workflow ### 1. Scan for Hardcoded Credentials Search for credential patterns in code: ```bash # Passwords grep -rn "password.*=.*['\"]" --include="*.py" --include="*.js" --include="*.vue" \ shopdb/ plugins/ frontend/src/ \ --exclude-dir=node_modules --exclude-dir=dist \ | grep -v "password.*input\|password.*field\|password.*type\|password.*param" # API keys grep -rn "api_key.*=.*['\"]" --include="*.py" shopdb/ plugins/ \ | grep -v "api_key.*None\|api_key.*config\|api_key.*os.environ" # Tokens grep -rn "token.*=.*['\"][a-zA-Z0-9]{20,}" --include="*.py" shopdb/ plugins/ # Database credentials grep -rn "mysql://.*:.*@" --include="*.py" --include="*.yml" . ``` **Acceptable patterns:** - `password = None` (default/placeholder) - `password = os.environ.get(...)` (from environment) - `password = config.get(...)` (from config) - `password_field = "..."` (field name, not value) **Flag as violations:** - `password = "admin123"` (hardcoded) - `api_key = "sk_live_..."` (real key) - `token = "eyJhbGciOi..."` (JWT token) - `mysql://root:password@localhost` (database URL with creds) ### 2. Check for Exposed Collector Keys Verify collector API keys not committed: ```bash # Check for shopdb_pat_ prefix (our collector keys) grep -rn "shopdb_pat_" --include="*.py" --include="*.js" --include="*.md" . \ --exclude-dir=.git # Check .env files not committed git ls-files | grep -E "\.env$|\.env\." # Check for common secret patterns grep -rn -E "(SECRET_KEY|COLLECTOR_KEY|API_KEY|PRIVATE_KEY).*=.*['\"][a-zA-Z0-9]{32,}" \ --include="*.py" shopdb/ plugins/ ``` **Acceptable:** - `.env.example` with placeholder values - Comments referencing environment variables - Memory files with redacted keys **Violations:** - `.env` file committed - Real collector key in code - API key in configuration file ### 3. Verify Certificate Validation Check that agent validates HTTPS certificates: ```bash # C# agent code grep -n "ServerCertificateValidationCallback" agent/ShopDbEndpointAgent/ # Should NOT find: # ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true; # This disables all certificate validation! # Python collector code grep -n "verify=False" shopdb/ plugins/ --include="*.py" grep -n "ssl._create_unverified_context" shopdb/ plugins/ --include="*.py" ``` **Acceptable:** - Development-only code with explicit comment - Test code with mock certificates **Violations:** - Production code with `verify=False` - Agent accepts any certificate without validation - No certificate pinning for critical endpoints ### 4. Check Authentication on Sensitive Endpoints Verify authentication required for sensitive operations: ```bash # Find Flask routes grep -rn "@.*route\|@bp.route" shopdb/ plugins/ --include="*.py" -A 3 # For each route, check if it has authentication: # - @requires_auth decorator # - @login_required decorator # - Manual current_user check ``` **Sensitive operations that MUST have auth:** - `/api/setup/*` (after initial setup) - `/api/collectors/*` (collector authentication) - `/api/admin/*` (admin only) - `/api/*/create` (asset creation) - `/api/*/update` (data modification) - `/api/*/delete` (data deletion) **Flag missing authentication:** ```python @bp.route('/api/endpoints/update', methods=['POST']) def update_endpoint(): # NO authentication check! data = request.json # ... update logic ``` **Correct patterns:** ```python @bp.route('/api/endpoints/update', methods=['POST']) @requires_auth # [X] Has authentication def update_endpoint(): ... @bp.route('/api/collectors/heartbeat', methods=['POST']) @requires_device_token # [X] Collector authentication def collector_heartbeat(): ... ``` ### 5. Scan for SQL Injection Vulnerabilities Check for raw SQL without parameterization: ```bash # Find string concatenation in SQL grep -rn "execute.*+\|execute.*format\|execute.*%" \ --include="*.py" shopdb/ plugins/ \ | grep -v "# safe:\|# parameterized" # Find f-strings in SQL grep -rn 'execute.*f".*{' --include="*.py" shopdb/ plugins/ # Find .format() in SQL grep -rn "execute.*\.format\(" --include="*.py" shopdb/ plugins/ ``` **Vulnerable patterns:** ```python # BAD: String concatenation query = "SELECT * FROM assets WHERE name = '" + user_input + "'" db.session.execute(query) # BAD: f-string query = f"SELECT * FROM users WHERE username = '{username}'" db.session.execute(query) # BAD: .format() query = "DELETE FROM assets WHERE id = {}".format(asset_id) ``` **Safe patterns:** ```python # GOOD: Parameterized query query = "SELECT * FROM assets WHERE name = :name" db.session.execute(query, {"name": user_input}) # GOOD: SQLAlchemy ORM Asset.query.filter_by(name=user_input).first() # GOOD: SQLAlchemy Core db.session.execute(select(Asset).where(Asset.name == user_input)) ``` ### 6. Verify HTTPS Enforcement Check that HTTP is not allowed in production: ```bash # Flask configuration grep -rn "PREFERRED_URL_SCHEME" shopdb/config.py # Should be: PREFERRED_URL_SCHEME = 'https' # Check for HTTP URLs in code grep -rn "http://.*5001\|http://.*api" --include="*.py" --include="*.js" . \ --exclude-dir=node_modules --exclude-dir=dist # Check Vite proxy config grep -n "target.*http:" frontend/vite.config.js ``` **Acceptable:** - Development Vite server (HTTP on 5173) - HTTP in proxy target (SSL termination) **Violations:** - Flask serving HTTP in production - API calls without HTTPS - Cookies without Secure flag ### 7. Check for Sensitive Data in Logs Scan for patterns that expose sensitive data: ```bash # Check for password logging grep -rn "logging.*password\|print.*password\|log.info.*password" \ --include="*.py" shopdb/ plugins/ \ | grep -v "password.*\*\*\*\|password.*redacted" # Check for API key logging grep -rn "logging.*api_key\|print.*api_key" --include="*.py" shopdb/ plugins/ # Check for full request logging (may contain sensitive headers) grep -rn "log.*request.headers\|print.*request.headers" \ --include="*.py" shopdb/ plugins/ ``` **Violations:** ```python # BAD: Password in logs logging.info(f"Login attempt: {username} / {password}") # BAD: API key exposed print(f"Using API key: {api_key}") # BAD: Full headers (may contain auth tokens) logger.debug(f"Request headers: {request.headers}") ``` **Safe patterns:** ```python # GOOD: Redacted password logging.info(f"Login attempt: {username} / ***") # GOOD: Partial key logger.debug(f"API key: {api_key[:8]}...") # GOOD: Specific header logger.debug(f"Content-Type: {request.headers.get('Content-Type')}") ``` ### 8. Validate Input Sanitization Check that user input is validated: ```bash # Find request.json usage without validation grep -rn "request.json\|request.form\|request.args" \ --include="*.py" shopdb/ plugins/ -A 5 \ | grep -v "validator\|schema\|validate\|is_valid" ``` **Look for:** - Direct use of `request.json['field']` without validation - No length limits on string inputs - No type checking on numeric inputs - No sanitization of file paths **Vulnerable:** ```python # BAD: No validation @bp.route('/api/assets', methods=['POST']) def create_asset(): data = request.json # No validation! asset = Asset(**data) # Directly creates from user input db.session.add(asset) ``` **Safe:** ```python # GOOD: Schema validation from marshmallow import Schema, fields, validate class AssetSchema(Schema): name = fields.Str(required=True, validate=validate.Length(max=255)) asset_type = fields.Str(required=True, validate=validate.OneOf(['computer', 'printer'])) @bp.route('/api/assets', methods=['POST']) def create_asset(): schema = AssetSchema() data = schema.load(request.json) # Validated! asset = Asset(**data) ``` ### 9. Summary Report **[X] No vulnerabilities found:** ``` Security scan complete: PASS Credential Scan: [X] No hardcoded passwords found [X] No API keys in code [X] No tokens exposed [X] Collector keys not committed Certificate Validation: [X] Agent validates certificates [X] No verify=False in production code Authentication: [X] All sensitive endpoints protected [X] Collector endpoints use device tokens [X] Admin endpoints require authentication SQL Injection: [X] All queries parameterized [X] No string concatenation in SQL [X] ORM used consistently HTTPS Enforcement: [X] Production uses HTTPS [X] Cookies have Secure flag [X] No HTTP URLs in production config Logging: [X] No passwords in logs [X] API keys redacted [X] Sensitive data masked Input Validation: [X] Schema validation on all POST/PUT endpoints [X] Length limits enforced [X] Type checking present No security issues detected! ``` **WARNING: Vulnerabilities found:** ``` Security scan: FAILED CRITICAL (block deployment): 1. Hardcoded admin password in tests/fixtures/users.py Line 15: password = "admin123" Impact: Test credential could be used in production Action: Use environment variable or generate random password 2. Collector API key committed in .env file File: .env (should be .gitignore'd) Impact: API key exposed in version control Action: Remove .env, regenerate key, add to .gitignore 3. Certificate validation disabled in agent File: agent/ShopDbEndpointAgent/ApiClient.cs:42 Code: ServerCertificateValidationCallback = (_, _, _, _) => true Impact: Agent accepts any certificate (MITM attack possible) Action: Implement proper certificate validation HIGH PRIORITY: 4. Missing authentication on /api/endpoints/update File: plugins/endpoints/api/routes.py:87 Impact: Anyone can update endpoint data Action: Add @requires_device_token decorator 5. SQL injection vulnerability in asset search File: shopdb/core/api/assets.py:45 Code: f"SELECT * FROM assets WHERE name LIKE '%{search}%'" Impact: SQL injection attack possible Action: Use parameterized query or ORM MEDIUM: 6. Password logged in authentication.py:23 Code: logger.info(f"Login: {username}/{password}") Impact: Passwords exposed in log files Action: Redact password: logger.info(f"Login: {username}/***") DO NOT DEPLOY until critical and high priority issues are fixed! Remediation guide: [[security-fix-implementation]] ``` ## Automated Remediation Some issues can be auto-fixed: **Remove hardcoded credentials:** ```bash # Replace hardcoded passwords with environment variables sed -i 's/password = ".*"/password = os.environ.get("TEST_PASSWORD", "changeme")/g' tests/fixtures/*.py ``` **Add .env to .gitignore:** ```bash echo ".env" >> .gitignore git rm --cached .env ``` **Add authentication decorator:** ```python # Before: @bp.route('/api/endpoints/update', methods=['POST']) def update_endpoint(): ... # After: @bp.route('/api/endpoints/update', methods=['POST']) @requires_device_token def update_endpoint(): ... ``` ## Related - [[security-audit-2026-09-11]] - Original audit findings - [[security-fix-implementation]] - Detailed remediation plans - `/pre-commit` - Include basic security checks in pre-commit - `/production-health` - Runtime security monitoring - ADR-016: Credential delivery (device tokens)