Skip to content
Merged
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
184 changes: 184 additions & 0 deletions .github/workflows/template-security-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
name: Weekly Template Security Scan

on:
schedule:
- cron: "0 23 * * 0"
workflow_dispatch:
inputs:
templates:
description: "Specific templates to scan (comma-separated, leave empty for all)"
required: false
default: ""

permissions:
contents: read
issues: write

jobs:
security-scan:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install pip-audit
run: pip install pip-audit

- name: Run security scan on templates
id: scan
run: |
TEMPLATE_DIR="src/fastapi_fastkit/fastapi_project_template"
RESULTS_FILE="security_scan_results.json"
SCAN_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Initialize results
echo '{' > $RESULTS_FILE
echo ' "scan_date": "'$SCAN_DATE'",' >> $RESULTS_FILE
echo ' "templates": [' >> $RESULTS_FILE

TEMPLATES_INPUT="${{ github.event.inputs.templates }}"
FIRST_TEMPLATE=true
TOTAL_VULNERABILITIES=0
AFFECTED_TEMPLATES=""

for template_dir in $TEMPLATE_DIR/fastapi-*/; do
template_name=$(basename "$template_dir")

# Skip if specific templates are requested and this isn't one
if [ -n "$TEMPLATES_INPUT" ]; then
if ! echo "$TEMPLATES_INPUT" | grep -q "$template_name"; then
continue
fi
fi

req_file="$template_dir/requirements.txt-tpl"
if [ -f "$req_file" ]; then
echo "🔍 Scanning $template_name..."

# Create temp requirements file
temp_req=$(mktemp)
cp "$req_file" "$temp_req"

# Run pip-audit and capture output
audit_output=$(pip-audit -r "$temp_req" --format json 2>/dev/null || echo '[]')
rm "$temp_req"

# Count vulnerabilities
vuln_count=$(echo "$audit_output" | python3 -c "import sys, json; data = json.load(sys.stdin); print(len(data))" 2>/dev/null || echo "0")

if [ "$vuln_count" -gt 0 ]; then
TOTAL_VULNERABILITIES=$((TOTAL_VULNERABILITIES + vuln_count))
AFFECTED_TEMPLATES="$AFFECTED_TEMPLATES $template_name"
echo "⚠️ Found $vuln_count vulnerabilities in $template_name"
else
echo "✅ No vulnerabilities in $template_name"
fi

# Add to JSON
if [ "$FIRST_TEMPLATE" = true ]; then
FIRST_TEMPLATE=false
else
echo ' ,' >> $RESULTS_FILE
fi

echo ' {' >> $RESULTS_FILE
echo ' "name": "'$template_name'",' >> $RESULTS_FILE
echo ' "vulnerability_count": '$vuln_count',' >> $RESULTS_FILE
echo ' "vulnerabilities": '$audit_output >> $RESULTS_FILE
echo ' }' >> $RESULTS_FILE
fi
done
Comment on lines +50 to +96
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop at line 50 does not properly handle the case where no templates are found or no templates match the input filter. If FIRST_TEMPLATE remains true after the loop, it will generate invalid JSON with a trailing comma in the templates array. Consider tracking whether any templates were processed and adjusting the JSON generation accordingly.

Copilot uses AI. Check for mistakes.

echo ' ],' >> $RESULTS_FILE
echo ' "total_vulnerabilities": '$TOTAL_VULNERABILITIES',' >> $RESULTS_FILE
echo ' "affected_templates": "'$(echo $AFFECTED_TEMPLATES | xargs)'"' >> $RESULTS_FILE
echo '}' >> $RESULTS_FILE

# Set outputs for later steps
echo "total_vulnerabilities=$TOTAL_VULNERABILITIES" >> $GITHUB_OUTPUT
echo "affected_templates=$AFFECTED_TEMPLATES" >> $GITHUB_OUTPUT
Comment on lines +36 to +105
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variables RESULTS_FILE, SCAN_DATE, TEMPLATE_DIR, and others should be quoted in shell commands to prevent word splitting issues. For example, line 41 should be echo '{' > "$RESULTS_FILE" instead of echo '{' > $RESULTS_FILE.

Copilot uses AI. Check for mistakes.

- name: Upload scan results
uses: actions/upload-artifact@v4
if: always()
with:
name: security-scan-results
path: security_scan_results.json
retention-days: 30

- name: Create Issue on Vulnerabilities
if: steps.scan.outputs.total_vulnerabilities != '0'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');

let issueBody = `## 🔒 Template Dependency Security Scan Results\n\n`;
issueBody += `**Scan Date:** ${new Date().toISOString()}\n`;
issueBody += `**Total Vulnerabilities Found:** ${{ steps.scan.outputs.total_vulnerabilities }}\n\n`;

try {
const results = JSON.parse(fs.readFileSync('security_scan_results.json', 'utf8'));

results.templates.forEach(template => {
if (template.vulnerability_count > 0) {
issueBody += `### ⚠️ ${template.name}\n\n`;
issueBody += `Found **${template.vulnerability_count}** vulnerabilities:\n\n`;
issueBody += `| Package | Installed | Fix Versions | Vulnerability ID |\n`;
issueBody += `|---------|-----------|--------------|------------------|\n`;

template.vulnerabilities.forEach(vuln => {
const fixVersions = vuln.fix_versions ? vuln.fix_versions.join(', ') : 'N/A';
issueBody += `| ${vuln.name} | ${vuln.version} | ${fixVersions} | ${vuln.id} |\n`;
});

issueBody += `\n`;
}
});

} catch (error) {
issueBody += `\nError reading detailed results: ${error.message}\n`;
}

issueBody += `\n---\n**Workflow:** [${context.workflow}](${context.payload.repository.html_url}/actions/runs/${context.runId})`;

// Determine severity label
const totalVulns = parseInt('${{ steps.scan.outputs.total_vulnerabilities }}');
let severityLabel = 'security: low';
if (totalVulns >= 10) {
severityLabel = 'security: critical';
} else if (totalVulns >= 5) {
severityLabel = 'security: high';
} else if (totalVulns >= 2) {
severityLabel = 'security: medium';
}

await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `🔒 Template Security Alert: ${totalVulns} vulnerabilities found - ${new Date().toISOString().split('T')[0]}`,
body: issueBody,
labels: ['security', severityLabel, 'automated', 'template']
});

- name: Report Summary
if: always()
run: |
echo "## 🔒 Security Scan Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

if [ "${{ steps.scan.outputs.total_vulnerabilities }}" = "0" ]; then
echo "✅ **No vulnerabilities found in any template!**" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ **Found ${{ steps.scan.outputs.total_vulnerabilities }} vulnerabilities**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Affected templates: ${{ steps.scan.outputs.affected_templates }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "A GitHub Issue has been created with detailed information." >> $GITHUB_STEP_SUMMARY
fi
27 changes: 26 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ Key commands for contributors:
- `make test` - Run all tests
- `make test-verbose` - Run tests with verbose output
- `make test-coverage` - Run tests with coverage report
- `make coverage-report` - Generate detailed coverage report (supports FORMAT=html/xml/json/all)

#### Installation and Building
- `make install-test` - Install package for testing (uninstall + reinstall)
Expand All @@ -109,6 +110,9 @@ Key commands for contributors:
- `make build-docs` - Build documentation
- `make serve-docs` - Serve documentation locally

#### Translation
- `make translate` - Translate documentation (supports LANG, PROVIDER, MODEL parameters)

### Development Workflow

1. **Before making changes:**
Expand Down Expand Up @@ -170,6 +174,13 @@ Run tests using these commands:
make test-coverage
```

4. **Detailed coverage report with options:**
```bash
make coverage-report # Terminal output
make coverage-report FORMAT=html # HTML report (opens in browser)
make coverage-report FORMAT=all # All formats (term, html, xml, json)
```

### Making PRs

Use these tags in PR title:
Expand Down Expand Up @@ -423,7 +434,21 @@ This migrates existing English docs to `docs/en/` and creates language directori

### Translating Documentation

#### Translate to Specific Language
#### Using Make Commands (Recommended)

```bash
# Translate all docs to all configured languages
make translate

# Translate to specific language
make translate LANG=ko

# Specify API provider and model
make translate LANG=ko PROVIDER=github MODEL=gpt-4o-mini
make translate LANG=ko PROVIDER=openai MODEL=gpt-4
```

#### Using Script Directly

```bash
# Translate all docs to Korean
Expand Down
20 changes: 19 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help install install-dev install-test uninstall test test-verbose lint format format-check clean build build-docs serve-docs version-update all
.PHONY: help install install-dev install-test uninstall test test-verbose lint format format-check clean build build-docs serve-docs version-update all translate coverage-report

# Default target
help: ## Show this help message
Expand Down Expand Up @@ -137,3 +137,21 @@ all: ## Run complete development workflow
$(MAKE) dev-setup
$(MAKE) dev-check
$(MAKE) build

# Translation commands
translate: ## Translate documentation - Usage: make translate LANG=ko PROVIDER=github MODEL=gpt-4o-mini
@echo "Starting translation..."
@CMD="python scripts/translate.py"; \
if [ -n "$(LANG)" ]; then CMD="$$CMD --target-lang $(LANG)"; fi; \
if [ -n "$(PROVIDER)" ]; then CMD="$$CMD --api-provider $(PROVIDER)"; fi; \
if [ -n "$(MODEL)" ]; then CMD="$$CMD --model $(MODEL)"; fi; \
echo "Running: $$CMD"; \
$$CMD

# Coverage report commands
coverage-report: ## Generate detailed coverage report - Usage: make coverage-report FORMAT=html
@if [ -z "$(FORMAT)" ]; then \
./scripts/coverage-report.sh; \
else \
./scripts/coverage-report.sh -f "$(FORMAT)"; \
fi
16 changes: 16 additions & 0 deletions docs/en/contributing/development-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ The project Makefile provides convenient commands for common development tasks:
| `make test-unit` | Run unit tests only |
| `make test-integration` | Run integration tests only |
| `make test-coverage` | Run tests with coverage report |
| `make coverage-report` | Generate detailed coverage report (FORMAT=html/xml/json/all) |
| `make test-watch` | Run tests in watch mode |

### Documentation Commands
Expand All @@ -201,6 +202,12 @@ The project Makefile provides convenient commands for common development tasks:
| `make docs-build` | Build documentation |
| `make docs-deploy` | Deploy documentation to GitHub Pages |

### Translation Commands

| Command | Description |
|---------|-----------|
| `make translate` | Translate documentation (LANG, PROVIDER, MODEL parameters) |

### Examples

<div class="termy">
Expand Down Expand Up @@ -231,6 +238,15 @@ src/cli.py 89 5 94%
src/templates.py 67 3 96%
--------------------------------------------
TOTAL 201 10 95%

# Generate HTML coverage report
$ make coverage-report FORMAT=html
🌐 Opening HTML coverage report in browser...

# Translate documentation to Korean
$ make translate LANG=ko PROVIDER=github MODEL=gpt-4o-mini
Starting translation...
Running: python scripts/translate.py --target-lang ko --api-provider github --model gpt-4o-mini
```

</div>
Expand Down
20 changes: 19 additions & 1 deletion docs/en/contributing/translation-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,25 @@ gh auth login

## Usage

### Translate All Documentation
### Using Make Commands (Recommended)

The easiest way to run translations:

```bash
# Translate all docs to all languages
make translate

# Translate to specific language
make translate LANG=ko

# Specify API provider and model
make translate LANG=ko PROVIDER=openai MODEL=gpt-4
make translate LANG=ko PROVIDER=github MODEL=gpt-4o-mini
```

### Using Script Directly

#### Translate All Documentation

Translate all documentation to all supported languages:

Expand Down
1 change: 1 addition & 0 deletions docs/ko/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{!CHANGELOG.md!}
Loading
Loading