CtrlK
BlogDocsLog inGet started
Tessl Logo

pantheon-ai/makefile-generator

Generate GNU Make build systems that define build targets, configure dependencies, set up phony targets, and implement parallel builds. Use when creating make/Makefile/.mk files, implementing compile rules, or building production-ready build automation for C/C++, Go, Python, and Java projects.

Overall
score

93%

Does it follow best practices?

Validation for skill structure

Overview
Skills
Evals
Files

security-guide.mdreferences/

Makefile Security Guide

Overview

This guide covers security best practices for Makefiles, including secrets management, input validation, shell injection prevention, and CI/CD security considerations.

Secrets Management

Never Commit Secrets

DO NOT hardcode credentials, API keys, or passwords in Makefiles:

# WRONG: Hardcoded credentials
DB_PASSWORD := mysecretpassword
AWS_SECRET := AKIAIOSFODNN7EXAMPLE

Use Environment Variables

Pass secrets via environment variables:

# CORRECT: Environment variable with validation
DB_PASSWORD ?= $(error DB_PASSWORD is not set)
AWS_SECRET_KEY ?= $(error AWS_SECRET_KEY is not set)

deploy:
	@echo "Deploying with credentials from environment..."
	./deploy.sh

Use .env Files (Not in Git)

# Include .env file if it exists (never commit .env!)
-include .env
export

# Ensure .gitignore contains .env
.PHONY: check-env
check-env:
	@grep -q '^\.env$$' .gitignore || echo "WARNING: Add .env to .gitignore!"

Secrets from External Sources

AWS Secrets Manager:

# Fetch secret at runtime, don't cache in Makefile variables
deploy:
	@DB_PASSWORD=$$(aws secretsmanager get-secret-value \
		--secret-id prod/db/password \
		--query SecretString --output text) && \
	./deploy.sh

HashiCorp Vault:

deploy:
	@DB_PASSWORD=$$(vault kv get -field=password secret/database) && \
	./deploy.sh

Shell Injection Prevention

Input Validation

Always validate user-provided variables:

# Validate PROJECT_NAME contains only safe characters
PROJECT_NAME := $(strip $(PROJECT_NAME))
ifneq ($(PROJECT_NAME),$(shell echo '$(PROJECT_NAME)' | tr -cd 'a-zA-Z0-9_-'))
$(error PROJECT_NAME contains invalid characters. Use only [a-zA-Z0-9_-])
endif

Quote Variables in Shell Commands

# WRONG: Unquoted variables - vulnerable to injection
process:
	./script.sh $(USER_INPUT)

# CORRECT: Quoted variables
process:
	./script.sh '$(USER_INPUT)'

Avoid Shell Expansion of User Input

# WRONG: Shell will interpret special characters
echo-input:
	@echo $(MESSAGE)

# SAFER: Use printf with proper quoting
echo-input:
	@printf '%s\n' '$(MESSAGE)'

Dangerous Patterns to Avoid

# NEVER do this - allows arbitrary command execution
run-command:
	$(USER_COMMAND)

# NEVER pipe untrusted input to shell
execute:
	echo $(INPUT) | sh

# NEVER use eval with user input
eval-input:
	@eval $(USER_INPUT)

Variable Expansion Security

Simple vs Recursive Expansion

# Use := for values that shouldn't be re-evaluated
SAFE_VALUE := $(shell whoami)

# = causes re-evaluation each time - potential for injection
# if EXTERNAL_VAR changes after assignment
UNSAFE_VALUE = $(EXTERNAL_VAR)

Dollar Sign Escaping

When working with passwords containing $:

# Password with $ sign - double the $ to escape
# If password is "pa$$word", set it as:
PASSWORD := pa$$$$word

# Or read from file where $ is already escaped
PASSWORD := $(shell cat .password | sed 's/\$$/\$\$\$\$/g')

File System Security

Path Traversal Prevention

# WRONG: User can specify "../../../etc/passwd"
read-file:
	cat $(FILE_PATH)

# SAFER: Validate path is within expected directory
SAFE_DIR := ./data
read-file:
	@case "$(FILE_PATH)" in \
		$(SAFE_DIR)/*) cat "$(FILE_PATH)" ;; \
		*) echo "ERROR: Invalid path" >&2; exit 1 ;; \
	esac

Secure Temporary Files

# Use mktemp for secure temporary files
process:
	@TMPFILE=$$(mktemp) && \
	trap 'rm -f "$$TMPFILE"' EXIT && \
	./generate-config > "$$TMPFILE" && \
	./process-config "$$TMPFILE"

File Permission Handling

# Set restrictive permissions on sensitive files
install-config:
	install -m 600 config.secret $(DESTDIR)/etc/myapp/

# Create directories with appropriate permissions
install-dirs:
	install -d -m 700 $(DESTDIR)/var/lib/myapp/secrets

CI/CD Security

Avoid Logging Secrets

# WRONG: Password visible in logs
deploy:
	curl -u user:$(PASSWORD) https://api.example.com

# CORRECT: Suppress command echo
deploy:
	@curl -u user:$(PASSWORD) https://api.example.com

# BEST: Use credential helper
deploy:
	@curl --netrc-file ~/.netrc https://api.example.com

Fail Securely

# Use strict mode
SHELL := bash
.SHELLFLAGS := -eu -o pipefail -c

# Ensure sensitive operations fail closed
deploy:
	@test -n "$(API_KEY)" || { echo "ERROR: API_KEY not set" >&2; exit 1; }
	@./deploy.sh

Environment Isolation

# Don't inherit all environment variables
# Only export what's needed
unexport HISTFILE
unexport AWS_SESSION_TOKEN

# Explicitly export required variables
export PATH
export HOME

Audit Logging

AUDIT_LOG := /var/log/makefile-audit.log

audit-log = @echo "$$(date -Iseconds) [$(1)] $(2)" >> $(AUDIT_LOG)

deploy: check-permissions
	$(call audit-log,DEPLOY,Starting deployment by $$USER)
	./deploy.sh
	$(call audit-log,DEPLOY,Deployment completed)

Network Security

Secure Downloads

# Always verify downloads
CHECKSUM_FILE := checksums.sha256

download:
	curl -fsSL -o package.tar.gz https://example.com/package.tar.gz
	sha256sum -c $(CHECKSUM_FILE)

# Or use GPG verification
download-verified:
	curl -fsSL -o package.tar.gz https://example.com/package.tar.gz
	curl -fsSL -o package.tar.gz.asc https://example.com/package.tar.gz.asc
	gpg --verify package.tar.gz.asc package.tar.gz

TLS/HTTPS Only

# Force HTTPS for all downloads
CURL_OPTS := --proto '=https' --tlsv1.2

download:
	curl $(CURL_OPTS) -fsSL -o file.txt https://example.com/file.txt

Container Security

Don't Build as Root

docker-build:
	docker build --build-arg USER_ID=$$(id -u) --build-arg GROUP_ID=$$(id -g) -t myapp .

# In Dockerfile, create non-root user

Scan Images for Vulnerabilities

IMAGE := myapp:latest

.PHONY: docker-scan
docker-scan: docker-build
	@if command -v trivy >/dev/null 2>&1; then \
		trivy image --exit-code 1 --severity HIGH,CRITICAL $(IMAGE); \
	else \
		echo "WARNING: trivy not found, skipping security scan"; \
	fi

Don't Pass Secrets as Build Args

# WRONG: Secret visible in image layers
docker-build:
	docker build --build-arg API_KEY=$(API_KEY) -t myapp .

# CORRECT: Use build secrets (BuildKit)
docker-build:
	DOCKER_BUILDKIT=1 docker build \
		--secret id=api_key,src=.api_key \
		-t myapp .

Secure Defaults

Modern Makefile Preamble

# Secure and strict Makefile configuration
SHELL := bash
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules

# Prevent accidental exposure
unexport HISTFILE

Require Explicit Targets

# Prevent running all targets by accident
.PHONY: all
all:
	@echo "Please specify a target. Run 'make help' for options."
	@exit 1

Security Checklist

Before committing a Makefile:

  • No hardcoded credentials, API keys, or passwords
  • Secrets loaded from environment or secret manager
  • .env file listed in .gitignore
  • User input is validated before use
  • Shell commands use proper quoting
  • No use of eval with external input
  • Downloads verified with checksums or signatures
  • Sensitive commands prefixed with @ to hide from logs
  • Temporary files created securely and cleaned up
  • File permissions are appropriately restrictive
  • Container builds don't expose secrets in layers

References

  • OWASP Command Injection
  • CWE-78: Improper Neutralization of Special Elements
  • GNU Make Security Considerations
  • Passing Credentials in GNU Make - Security Stack Exchange
  • GitGuardian - Secure Your Secrets with .env

Install with Tessl CLI

npx tessl i pantheon-ai/makefile-generator@0.1.1

references

makefile-structure.md

optimization-guide.md

patterns-guide.md

README.md

security-guide.md

targets-guide.md

variables-guide.md

SKILL.md

tile.json