AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Secure Coding

skill-aeyeops-aeo-skill-marketplace-secure-coding · by AeyeOps

|

— No reviews yet
0 installs
34 views
0.0% view→install

Install

$ agentstack add skill-aeyeops-aeo-skill-marketplace-secure-coding

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • ✓ Prompt-injection patterns
  • ✓ Secret / credential exfiltration
  • ✓ Dangerous shell & filesystem operations
  • ✓ Untrusted network calls
  • ✓ Known-malicious package signatures

What it can access

  • ✓ Network access No
  • ● Filesystem access Used
  • ✓ Shell / process execution No
  • ● Environment & secrets Used
  • ● Dynamic code execution Used

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-aeyeops-aeo-skill-marketplace-secure-coding)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
● 2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Secure Coding? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Secure Coding Practices

Input Validation

Validation Strategy

Validate at system boundaries:
1. All user input (forms, query params, headers, file uploads)
2. External API responses (don't trust third-party data)
3. Configuration files loaded at runtime
4. Data read from databases (defense in depth)

Validation approach:
- Allowlist over denylist (define what IS valid, not what isn't)
- Validate type, length, range, format, and business rules
- Reject invalid input — don't try to "fix" it
- Validate on the server side (client-side is for UX, not security)

Common Validations

# Email validation
import re
EMAIL_PATTERN = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")

def validate_email(email: str) -> str:
    email = email.strip().lower()
    if len(email) > 254:
        raise ValueError("Email too long")
    if not EMAIL_PATTERN.match(email):
        raise ValueError("Invalid email format")
    return email

# Numeric range validation
def validate_quantity(qty: int) -> int:
    if not isinstance(qty, int):
        raise TypeError("Quantity must be an integer")
    if qty  10000:
        raise ValueError("Quantity must be between 1 and 10000")
    return qty

# String sanitization
def validate_username(username: str) -> str:
    username = username.strip()
    if len(username)  30:
        raise ValueError("Username must be 3-30 characters")
    if not re.match(r"^[a-zA-Z0-9_-]+$", username):
        raise ValueError("Username may only contain letters, numbers, hyphens, underscores")
    return username

File Upload Validation

ALLOWED_EXTENSIONS = {".pdf", ".png", ".jpg", ".jpeg", ".docx"}
MAX_FILE_SIZE = 10 * 1024 * 1024  # 10 MB

def validate_upload(file) -> None:
    # Check file size
    if file.size > MAX_FILE_SIZE:
        raise ValueError("File too large (max 10MB)")

    # Check extension (use allowlist)
    ext = os.path.splitext(file.filename)[1].lower()
    if ext not in ALLOWED_EXTENSIONS:
        raise ValueError(f"File type not allowed: {ext}")

    # Check MIME type (don't trust Content-Type header alone)
    import magic
    mime = magic.from_buffer(file.read(2048), mime=True)
    file.seek(0)
    ALLOWED_MIMES = {"application/pdf", "image/png", "image/jpeg"}
    if mime not in ALLOWED_MIMES:
        raise ValueError(f"File content type not allowed: {mime}")

    # Generate safe filename (never use user-provided filename for storage)
    import uuid
    safe_name = f"{uuid.uuid4()}{ext}"
    return safe_name

Output Encoding

Prevent XSS (Cross-Site Scripting)

Rule: Encode output based on the context where it appears.

HTML context:    Encode  & " '   → < > & " '
Attribute:       Encode all non-alphanumeric characters
JavaScript:      Encode all non-alphanumeric characters as \xHH
URL:             Use percent-encoding for parameter values
CSS:             Encode all non-alphanumeric characters as \HHHHHH
# VULNERABLE: Raw user input in HTML
html = f"Welcome, {username}!"

# FIXED: Use template engine with auto-escaping (Jinja2)
# Jinja2 auto-escapes by default:
# {{ username }}  →  renders as <script> not 

# FIXED: Manual escaping when needed
from markupsafe import escape
html = f"Welcome, {escape(username)}!"

# For JavaScript context:
import json
safe_js = json.dumps(user_data)  # JSON encoding is safe for JS context
script = f"var data = {safe_js};"

Content Security Policy (CSP)

Set CSP header to prevent inline scripts and unauthorized sources:

# Strict CSP (recommended)
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self'

What each directive does:
  default-src 'self'   — Only load resources from same origin
  script-src 'self'    — Only run scripts from same origin (blocks inline)
  object-src 'none'    — Block Flash, Java, other plugins
  base-uri 'self'      — Prevent base tag hijacking
  form-action 'self'   — Forms can only submit to same origin

Secrets Management

Never Hard-Code Secrets

# VULNERABLE: Secrets in source code
API_KEY = "sk-1234567890abcdef"
DB_PASSWORD = "super_secret_password"

# FIXED: Load from environment
API_KEY = os.environ["API_KEY"]
DB_PASSWORD = os.environ["DB_PASSWORD"]

# FIXED: Use a secrets manager
from aws_secrets import get_secret
API_KEY = get_secret("my-app/api-key")

Secret Storage Hierarchy

Best to worst:
1. Secrets manager (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault)
   - Automatic rotation, access control, audit logging
2. Environment variables (set by deployment system)
   - Not in code, but visible via /proc/environ, ps
3. Encrypted config files (decrypted at deploy time)
   - Key management becomes the problem
4. .env files (gitignored, never committed)
   - OK for local development only

NEVER:
- Commit secrets to git (even if you delete them later — they're in history)
- Log secrets (mask in log output)
- Pass secrets via URL parameters (logged by proxies)
- Store secrets in client-side code (JavaScript, mobile apps)

Handling Secret Exposure

If a secret is committed to git:
1. Rotate the secret IMMEDIATELY (generate new key/password)
2. Revoke the old secret in the service provider
3. Remove from git history: git filter-branch or BFG Repo-Cleaner
4. Force-push to all branches (after coordination with team)
5. Audit access logs for unauthorized use during exposure window
6. Add the secret pattern to .gitignore and pre-commit hooks

Authentication Patterns

Password Handling

import bcrypt

def hash_password(password: str) -> str:
    """Hash a password for storage. Never store plaintext."""
    return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt(rounds=12)).decode("utf-8")

def verify_password(password: str, hashed: str) -> bool:
    """Verify a password against its hash. Use constant-time comparison."""
    return bcrypt.checkpw(password.encode("utf-8"), hashed.encode("utf-8"))
Password policy:
- Minimum 8 characters (NIST recommends no maximum under 64)
- Check against breach databases (Have I Been Pwned API)
- Do NOT require special characters or forced rotation (per NIST 800-63B)
- Allow paste into password fields (password managers)
- Provide strength meter for user feedback

Token-Based Authentication (JWT)

import jwt
from datetime import datetime, timedelta, timezone

SECRET_KEY = os.environ["JWT_SECRET"]
ALGORITHM = "HS256"

def create_token(user_id: int) -> str:
    payload = {
        "sub": str(user_id),
        "iat": datetime.now(timezone.utc),
        "exp": datetime.now(timezone.utc) + timedelta(hours=1),
        "jti": secrets.token_urlsafe(16),  # Unique token ID for revocation
    }
    return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)

def verify_token(token: str) -> dict:
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except jwt.ExpiredSignatureError:
        raise AuthError("Token expired")
    except jwt.InvalidTokenError:
        raise AuthError("Invalid token")
JWT best practices:
- [ ] Use short expiration (15 min for access tokens)
- [ ] Use refresh tokens (longer-lived) stored securely
- [ ] Include jti (JWT ID) for revocation capability
- [ ] Validate algorithm on decode (prevent "none" algorithm attack)
- [ ] Store tokens in httpOnly, secure cookies (not localStorage)
- [ ] Use asymmetric keys (RS256) in distributed systems

Session Management

Secure session configuration:
- [ ] Generate session ID with secrets.token_urlsafe(32)
- [ ] Set cookie flags: HttpOnly, Secure, SameSite=Lax
- [ ] Regenerate session ID after login (prevent session fixation)
- [ ] Invalidate session on logout (server-side deletion)
- [ ] Set absolute timeout (e.g., 8 hours) and idle timeout (e.g., 30 min)
- [ ] Store session data server-side (not in the cookie itself)

Authorization Patterns

Role-Based Access Control (RBAC)

from functools import wraps

PERMISSIONS = {
    "admin": {"read", "write", "delete", "manage_users"},
    "editor": {"read", "write"},
    "viewer": {"read"},
}

def require_permission(permission: str):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, current_user, **kwargs):
            user_permissions = PERMISSIONS.get(current_user.role, set())
            if permission not in user_permissions:
                raise PermissionError(f"Missing permission: {permission}")
            return func(*args, current_user=current_user, **kwargs)
        return wrapper
    return decorator

@require_permission("delete")
def delete_record(record_id: int, current_user):
    # Only admins reach here
    db.delete(record_id)

Resource-Level Authorization

def get_document(doc_id: int, current_user: User) -> Document:
    doc = db.query(Document).get(doc_id)
    if doc is None:
        raise NotFoundError("Document not found")  # Don't reveal existence to unauthorized
    if doc.owner_id != current_user.id and not current_user.is_admin:
        raise NotFoundError("Document not found")  # Same error — don't leak access info
    return doc

Cryptography Basics

When to Use What

Hashing (one-way, not reversible):
  Passwords    → bcrypt, scrypt, Argon2 (with salt, high cost factor)
  Integrity    → SHA-256, SHA-3 (verify data hasn't been tampered with)
  NEVER use    → MD5, SHA-1 (broken for security purposes)

Symmetric encryption (same key for encrypt and decrypt):
  Data at rest → AES-256-GCM (authenticated encryption)
  Use case     → Encrypting files, database fields, backups
  Key mgmt     → Store keys in secrets manager, rotate regularly

Asymmetric encryption (public/private key pair):
  Data in transit → TLS (HTTPS)
  Signatures      → RSA, Ed25519 (verify sender identity)
  Use case        → Key exchange, digital signatures, JWT (RS256)

Secure Random Numbers

import secrets

# Generate random token
token = secrets.token_urlsafe(32)     # URL-safe base64 string
token = secrets.token_hex(32)          # Hex string

# Generate random integer
otp = secrets.randbelow(1000000)       # 0 to 999999

# NEVER use for security:
import random
random.randint(0, 999999)  # Predictable! Not cryptographically secure

Secure Defaults

Default-deny:
- Access denied unless explicitly granted
- Features disabled unless explicitly enabled
- Ports closed unless explicitly opened

Principle of least privilege:
- Services run with minimum required permissions
- Database users have minimum required grants
- API keys scoped to minimum required capabilities
- File permissions set to minimum needed (644 files, 755 dirs)

Fail securely:
- On error, deny access (don't fall through to allow)
- On exception, return generic error to user, log details internally
- On timeout, treat as failure (don't assume success)

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.