doc #118
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: PHP Tests & Coverage | |
on: | |
push: | |
branches: [ main, master ] | |
pull_request: | |
branches: [ main, master ] | |
permissions: | |
contents: write | |
pages: write | |
jobs: | |
test-and-build: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v3 | |
- name: Setup PHP | |
uses: shivammathur/setup-php@v2 | |
with: | |
php-version: '8.2' | |
extensions: mbstring, xml, xdebug | |
coverage: xdebug | |
tools: composer:v2 | |
- name: Get composer cache directory | |
id: composer-cache-dir | |
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT | |
- name: Cache Composer dependencies | |
uses: actions/cache@v3 | |
id: composer-cache | |
with: | |
path: ${{ steps.composer-cache-dir.outputs.dir }} | |
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} | |
restore-keys: ${{ runner.os }}-composer- | |
- name: Install dependencies | |
run: | | |
composer install --no-progress --prefer-dist --optimize-autoloader | |
composer require --dev emuse/behat-html-formatter | |
- name: Run test suite with coverage | |
run: | | |
echo "Configurando cobertura..." | |
mkdir -p coverage | |
echo "Ejecutando PHPUnit con configuración local..." | |
XDEBUG_MODE=coverage ./vendor/bin/phpunit --configuration phpunit.xml || true | |
echo "Copiando reportes a public..." | |
mkdir -p public/coverage | |
cp -r coverage/html/* public/coverage/ | |
# Verificar la generación | |
if [ -d "public/coverage" ] && [ -f "public/coverage/index.html" ]; then | |
echo "✓ Reporte de cobertura generado correctamente" | |
ls -la public/coverage/ | |
else | |
echo "✗ Error al generar el reporte" | |
echo "Contenido actual:" | |
ls -R public/ | |
fi | |
- name: Verify Coverage Report | |
run: | | |
echo "Verificando reporte de cobertura..." | |
if [ -f "public/coverage/index.html" ]; then | |
echo "✓ El reporte de cobertura se generó correctamente" | |
ls -l public/coverage/ | |
else | |
echo "✗ El reporte de cobertura NO se generó" | |
echo "Contenido de public/:" | |
ls -R public/ | |
exit 1 | |
fi | |
- name: Run Infection tests | |
run: | | |
echo "Ejecutando Infection tests..." | |
# Crear directorios necesarios | |
echo "Creando directorios..." | |
mkdir -p reports/mutations | |
mkdir -p coverage/coverage-xml | |
mkdir -p public/mutations | |
# Crear archivo de configuración de Infection | |
echo "Creando archivo de configuración..." | |
echo '{ | |
"source": { | |
"directories": [ | |
"src" | |
], | |
"excludes": [ | |
"src/views" | |
] | |
}, | |
"logs": { | |
"text": "reports/mutations/infection.log", | |
"html": "reports/mutations/index.html", | |
"summary": "reports/mutations/summary.log", | |
"json": "reports/mutations/infection.json", | |
"perMutator": "reports/mutations/per-mutator.md" | |
}, | |
"mutators": { | |
"@default": true | |
}, | |
"testFramework": "phpunit", | |
"bootstrap": "./vendor/autoload.php", | |
"initialTestsPhpOptions": "-d xdebug.mode=coverage", | |
"testFrameworkOptions": "--testsuite=Unit,Mutation", | |
"phpUnit": { | |
"configDir": ".", | |
"customPath": "./vendor/bin/phpunit" | |
}, | |
"timeout": 10, | |
"minMsi": 60, | |
"minCoveredMsi": 60 | |
}' > infection.json.dist | |
# Generar cobertura XML primero | |
echo "Generando cobertura XML..." | |
XDEBUG_MODE=coverage vendor/bin/phpunit \ | |
--coverage-xml=coverage/coverage-xml \ | |
--log-junit=coverage/junit.xml \ | |
--coverage-filter=src || true | |
# Ejecutar PHPUnit con testdox | |
echo "Ejecutando PHPUnit..." | |
vendor/bin/phpunit --testdox || true | |
# Ejecutar Infection | |
echo "Ejecutando análisis de mutación..." | |
XDEBUG_MODE=coverage vendor/bin/infection \ | |
--threads=4 \ | |
--only-covered \ | |
--skip-initial-tests \ | |
--coverage=coverage \ | |
--ignore-msi-with-no-mutations \ | |
--no-progress || true | |
# Verificar y copiar reportes | |
echo "Verificando y copiando reportes..." | |
if [ -d "reports/mutations" ] && [ "$(ls -A reports/mutations)" ]; then | |
echo "Copiando reportes a public/mutations..." | |
cp -r reports/mutations/* public/mutations/ | |
echo "✓ Reportes copiados correctamente" | |
ls -l public/mutations/ | |
else | |
echo "✗ No se encontraron reportes para copiar" | |
echo "Contenido de reports/mutations:" | |
ls -la reports/mutations/ | |
fi | |
- name: Verify Mutations Report | |
run: | | |
echo "Verificando reportes de mutations..." | |
if [ -d "public/mutations" ]; then | |
echo "Contenido del directorio mutations:" | |
ls -la public/mutations/ | |
# Renombrar infection.html a index.html | |
if [ -f "public/mutations/infection.html" ]; then | |
mv public/mutations/infection.html public/mutations/index.html | |
echo "✓ Archivo infection.html renombrado a index.html" | |
fi | |
# Verificar cada tipo de reporte | |
for file in index.html infection.log summary.log infection.json per-mutator.md; do | |
if [ -f "public/mutations/$file" ]; then | |
echo "✓ $file existe" | |
else | |
echo "✗ $file no existe" | |
fi | |
done | |
else | |
echo "✗ El directorio mutations NO existe" | |
echo "Contenido de public/:" | |
ls -R public/ | |
fi | |
- name: Update Behat configuration | |
run: | | |
sed -i 's/Features/features/g' behat.yml | |
sed -i 's/Bootstrap/bootstrap/g' behat.yml | |
- name: Verify directory structure | |
run: | | |
echo "Verificando estructura actual..." | |
ls -R tests/BDD/ | |
echo "Creando estructura de directorios si no existe..." | |
mkdir -p tests/BDD/features/bootstrap | |
- name: Run Behat tests | |
run: | | |
mkdir -p public/bdd | |
composer dump-autoload | |
vendor/bin/behat --config behat.yml --format pretty --format html --out std --out public/bdd || true | |
- name: Generate Sonar Report | |
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' | |
run: | | |
# Obtener datos de SonarCloud | |
SONAR_DATA=$(curl -s "https://sonarcloud.io/api/measures/component?component=JosueUPT_CalidadU2&metricKeys=bugs,vulnerabilities,code_smells,coverage,duplicated_lines_density,complexity,ncloc,cognitive_complexity,comment_lines_density,security_rating,reliability_rating,sqale_rating,development_cost,security_remediation_effort,reliability_remediation_effort,sqale_index,confirmed_issues,effort_to_reach_maintainability_rating_a") | |
# Extraer valores | |
BUGS=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "bugs") | .value') | |
VULNERABILITIES=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "vulnerabilities") | .value') | |
CODE_SMELLS=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "code_smells") | .value') | |
COVERAGE=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "coverage") | .value') | |
DUPLICATION=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "duplicated_lines_density") | .value') | |
COMPLEXITY=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "complexity") | .value') | |
LINES=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "ncloc") | .value') | |
COGNITIVE_COMPLEXITY=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "cognitive_complexity") | .value') | |
COMMENT_DENSITY=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "comment_lines_density") | .value') | |
SECURITY_RATING=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "security_rating") | .value') | |
RELIABILITY_RATING=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "reliability_rating") | .value') | |
MAINTAINABILITY_RATING=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "sqale_rating") | .value') | |
DEVELOPMENT_COST=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "development_cost") | .value') | |
SECURITY_EFFORT=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "security_remediation_effort") | .value') | |
RELIABILITY_EFFORT=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "reliability_remediation_effort") | .value') | |
TECH_DEBT=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "sqale_index") | .value') | |
CONFIRMED_ISSUES=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "confirmed_issues") | .value') | |
EFFORT_TO_A=$(echo $SONAR_DATA | jq -r '.component.measures[] | select(.metric == "effort_to_reach_maintainability_rating_a") | .value') | |
# Verificar que los valores se obtuvieron | |
echo "Valores obtenidos:" | |
echo "Bugs: $BUGS" | |
echo "Coverage: $COVERAGE" | |
echo "Code Smells: $CODE_SMELLS" | |
mkdir -p public/sonar | |
echo "<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset=\"UTF-8\"> | |
<title>Análisis SonarCloud</title> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
margin: 40px; | |
background-color: #f5f5f5; | |
} | |
.container { | |
max-width: 1200px; | |
margin: 0 auto; | |
padding: 20px; | |
background-color: white; | |
border-radius: 10px; | |
box-shadow: 0 0 10px rgba(0,0,0,0.1); | |
} | |
.metric-grid { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
gap: 20px; | |
margin-top: 20px; | |
} | |
.metric-card { | |
background: #f8f9fa; | |
padding: 20px; | |
border-radius: 8px; | |
text-align: center; | |
} | |
.metric-title { | |
color: #666; | |
font-size: 1.1em; | |
margin-bottom: 10px; | |
} | |
.metric-value { | |
font-size: 2em; | |
font-weight: bold; | |
color: #333; | |
} | |
.metric-detail { | |
font-size: 0.9em; | |
color: #666; | |
margin-top: 5px; | |
} | |
.good { color: #28a745; } | |
.warning { color: #ffc107; } | |
.danger { color: #dc3545; } | |
.info { color: #17a2b8; } | |
h1 { | |
text-align: center; | |
color: #333; | |
margin-bottom: 30px; | |
} | |
.section-title { | |
margin-top: 30px; | |
color: #444; | |
border-bottom: 2px solid #eee; | |
padding-bottom: 10px; | |
} | |
</style> | |
</head> | |
<body> | |
<div class=\"container\"> | |
<h1>Análisis Detallado de Calidad del Código</h1> | |
<h2 class=\"section-title\">Métricas Principales</h2> | |
<div class=\"metric-grid\"> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Cobertura de Código</div> | |
<div class=\"metric-value info\">$COVERAGE%</div> | |
<div class=\"metric-detail\">$(echo \"100 - $COVERAGE\" | bc)% sin cobertura</div> | |
</div> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Bugs</div> | |
<div class=\"metric-value good\">$BUGS</div> | |
<div class=\"metric-detail\">Problemas detectados</div> | |
</div> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Code Smells</div> | |
<div class=\"metric-value warning\">$CODE_SMELLS</div> | |
<div class=\"metric-detail\">Oportunidades de mejora</div> | |
</div> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Duplicación</div> | |
<div class=\"metric-value good\">$DUPLICATION%</div> | |
<div class=\"metric-detail\">Código duplicado</div> | |
</div> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Vulnerabilidades</div> | |
<div class=\"metric-value good\">$VULNERABILITIES</div> | |
<div class=\"metric-detail\">Vulnerabilidades detectadas</div> | |
</div> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Líneas de Código</div> | |
<div class=\"metric-value info\">$LINES</div> | |
<div class=\"metric-detail\">Total de líneas</div> | |
</div> | |
</div> | |
<h2 class=\"section-title\">Métricas Avanzadas</h2> | |
<div class=\"metric-grid\"> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Complejidad Ciclomática</div> | |
<div class=\"metric-value info\">$COMPLEXITY</div> | |
<div class=\"metric-detail\">Total del proyecto</div> | |
</div> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Complejidad Cognitiva</div> | |
<div class=\"metric-value info\">$COGNITIVE_COMPLEXITY</div> | |
<div class=\"metric-detail\">Dificultad de entendimiento</div> | |
</div> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Densidad de Comentarios</div> | |
<div class=\"metric-value info\">$COMMENT_DENSITY%</div> | |
<div class=\"metric-detail\">Código documentado</div> | |
</div> | |
</div> | |
<h2 class=\"section-title\">Calificaciones</h2> | |
<div class=\"metric-grid\"> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Seguridad</div> | |
<div class=\"metric-value good\">A</div> | |
<div class=\"metric-detail\">Esfuerzo: $SECURITY_EFFORT min</div> | |
</div> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Confiabilidad</div> | |
<div class=\"metric-value good\">A</div> | |
<div class=\"metric-detail\">Esfuerzo: $RELIABILITY_EFFORT min</div> | |
</div> | |
<div class=\"metric-card\"> | |
<div class=\"metric-title\">Mantenibilidad</div> | |
<div class=\"metric-value good\">A</div> | |
<div class=\"metric-detail\">Deuda: $TECH_DEBT min</div> | |
</div> | |
</div> | |
<div style=\"text-align: right; margin-top: 30px; color: #666;\"> | |
Última actualización: $(date \"+%Y-%m-%d %H:%M:%S\") | |
</div> | |
</div> | |
</body> | |
</html>" > public/sonar/index.html | |
- name: Verify Sonar Report | |
run: | | |
echo "Verificando reporte de Sonar..." | |
if [ -f "public/sonar/index.html" ]; then | |
echo "✓ El archivo sonar/index.html existe" | |
ls -l public/sonar/ | |
else | |
echo "✗ El archivo sonar/index.html NO existe" | |
echo "Contenido de public/:" | |
ls -R public/ | |
fi | |
- name: Install Semgrep | |
run: | | |
python -m pip install semgrep | |
semgrep --version | |
- name: Semgrep Scan | |
run: | | |
# Crear directorio para resultados temporales | |
mkdir -p temp_results | |
echo "🔍 ANÁLISIS DETALLADO DE SEGURIDAD EN CONTROLLERS" | |
echo "==============================================" | |
# Ejecutar scan y guardar resultados | |
semgrep scan \ | |
--config "p/php" \ | |
--config "p/security-audit" \ | |
--config "p/owasp-top-ten" \ | |
--config "p/ci" \ | |
--no-git-ignore \ | |
--max-target-bytes=5000000 \ | |
--no-rewrite-rule-ids \ | |
--include "src/Controllers/*.php" \ | |
--severity ERROR \ | |
--severity WARNING \ | |
--json > semgrep_results.json | |
# Guardar métricas generales | |
total_files=$(ls src/Controllers/*.php | wc -l) | |
total_lines=$(find src/Controllers -name "*.php" -exec wc -l {} + | tail -1 | awk '{print $1}') | |
affected_lines=$(jq -r '.results | length' semgrep_results.json) | |
safe_percentage=$(echo "scale=2; 100 - ($affected_lines * 100 / $total_lines)" | bc) | |
# Guardar métricas en archivo | |
echo "$total_files" > temp_results/total_files | |
echo "$total_lines" > temp_results/total_lines | |
echo "$safe_percentage" > temp_results/safe_percentage | |
# Analizar cada controlador y guardar resultados | |
for file in src/Controllers/*.php; do | |
filename=$(basename $file) | |
# Crear archivo temporal para cada controlador | |
echo "=== $filename ===" > "temp_results/$filename.txt" | |
# Guardar líneas totales | |
wc -l < "$file" >> "temp_results/$filename.txt" | |
# Guardar funciones detectadas | |
echo "=== FUNCIONES ===" >> "temp_results/$filename.txt" | |
grep -n "function" "$file" >> "temp_results/$filename.txt" | |
# Guardar patrones | |
echo "=== PATRONES ===" >> "temp_results/$filename.txt" | |
echo "POST: $(grep -c "\$_POST" "$file")" >> "temp_results/$filename.txt" | |
echo "GET: $(grep -c "\$_GET" "$file")" >> "temp_results/$filename.txt" | |
echo "SQL: $(grep -c "query(" "$file")" >> "temp_results/$filename.txt" | |
echo "Validaciones: $(grep -c "validate\|sanitize" "$file")" >> "temp_results/$filename.txt" | |
echo "Try-Catch: $(grep -c "try {" "$file")" >> "temp_results/$filename.txt" | |
done | |
- name: Update Semgrep HTML Report | |
run: | | |
mkdir -p public/semgrep | |
# Primero creamos las variables con los datos | |
TOTAL_FILES=$(cat temp_results/total_files) | |
TOTAL_LINES=$(cat temp_results/total_lines) | |
SAFE_PERCENTAGE=$(cat temp_results/safe_percentage) | |
CURRENT_DATE=$(date "+%Y-%m-%d %H:%M:%S") | |
# Generamos el contenido de los hallazgos | |
FINDINGS_CONTENT="" | |
for file in temp_results/*.txt; do | |
[[ $(basename "$file") == "total_files" ]] && continue | |
[[ $(basename "$file") == "total_lines" ]] && continue | |
[[ $(basename "$file") == "safe_percentage" ]] && continue | |
filename=$(head -n 1 "$file" | cut -d "=" -f2 | tr -d " ") | |
lines=$(sed -n "2p" "$file") | |
# Formatear funciones para mejor legibilidad | |
functions=$(sed -n "/=== FUNCIONES ===/,/=== PATRONES ===/p" "$file" | grep -v "===" | while read -r line; do | |
echo "<div class=\"function-item\">$line</div>" | |
done) | |
# Formatear patrones para mejor legibilidad | |
patterns=$(tail -n 5 "$file" | while read -r line; do | |
echo "<span class=\"pattern-item\">$line</span>" | |
done) | |
FINDINGS_CONTENT+="<div class=\"finding\">" | |
FINDINGS_CONTENT+="<div class=\"file-header\">$filename</div>" | |
FINDINGS_CONTENT+="<div class=\"issue\">" | |
FINDINGS_CONTENT+="<div class=\"issue-title\">Análisis de Seguridad</div>" | |
FINDINGS_CONTENT+="<div class=\"details\">" | |
FINDINGS_CONTENT+="<div class=\"metrics-grid\">" | |
FINDINGS_CONTENT+="<div class=\"metric\">Líneas totales: $lines</div>" | |
FINDINGS_CONTENT+="<div class=\"patterns\">$patterns</div>" | |
FINDINGS_CONTENT+="</div>" | |
FINDINGS_CONTENT+="<h4>Funciones detectadas:</h4>" | |
FINDINGS_CONTENT+="<div class=\"functions-list\">" | |
FINDINGS_CONTENT+="$functions" | |
FINDINGS_CONTENT+="</div>" | |
FINDINGS_CONTENT+="</div>" | |
FINDINGS_CONTENT+="</div>" | |
FINDINGS_CONTENT+="</div>" | |
done | |
# Ahora generamos el HTML usando las variables | |
cat > public/semgrep/index.html << EOL | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Análisis de Seguridad Semgrep</title> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
margin: 40px; | |
background-color: #f5f5f5; | |
} | |
.metrics-grid { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
gap: 10px; | |
margin: 15px 0; | |
} | |
.metric { | |
background: #f8f9fa; | |
padding: 10px; | |
border-radius: 4px; | |
font-weight: bold; | |
} | |
.patterns { | |
display: flex; | |
flex-wrap: wrap; | |
gap: 10px; | |
} | |
.pattern-item { | |
background: #e9ecef; | |
padding: 5px 10px; | |
border-radius: 4px; | |
font-size: 0.9em; | |
} | |
.functions-list { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
gap: 10px; | |
margin-top: 10px; | |
} | |
.function-item { | |
background: #f8f9fa; | |
padding: 8px; | |
border-radius: 4px; | |
font-family: 'Courier New', monospace; | |
font-size: 0.9em; | |
border-left: 3px solid #3498db; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>Análisis de Seguridad con Semgrep</h1> | |
<div class="summary"> | |
<h2>Resumen del Escaneo</h2> | |
<div class="stats"> | |
<div class="stat-card"> | |
<h3>Archivos Analizados</h3> | |
<p>${TOTAL_FILES} archivos</p> | |
</div> | |
<div class="stat-card"> | |
<h3>Total Líneas</h3> | |
<p>${TOTAL_LINES} líneas</p> | |
</div> | |
<div class="stat-card"> | |
<h3>Código Seguro</h3> | |
<p>${SAFE_PERCENTAGE}%</p> | |
</div> | |
</div> | |
</div> | |
<h2>Hallazgos por Archivo</h2> | |
${FINDINGS_CONTENT} | |
<div class="summary"> | |
<h2>Notas Adicionales</h2> | |
<ul> | |
<li>Análisis completado: ${CURRENT_DATE}</li> | |
<li>Total archivos analizados: ${TOTAL_FILES}</li> | |
<li>Total líneas de código: ${TOTAL_LINES}</li> | |
</ul> | |
</div> | |
</div> | |
</body> | |
</html> | |
EOL | |
# Limpiar archivos temporales | |
rm -rf temp_results | |
- name: Install Snyk | |
run: npm install -g snyk | |
- name: Run Snyk Analysis | |
env: | |
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
run: | | |
mkdir -p public/snyk | |
echo "🔍 Analizando archivos en src/Controllers..." | |
# Obtener lista de archivos PHP en Controllers | |
CONTROLLERS=$(find src/Controllers -name "*.php") | |
# Crear array para almacenar resultados | |
echo "[]" > controller_analysis.json | |
# Analizar cada controlador | |
for controller in $CONTROLLERS; do | |
echo "📝 Analizando $controller..." | |
# Análisis de seguridad del archivo | |
ANALYSIS=$(snyk code test "$controller" --json || true) | |
# Guardar resultado | |
echo "$ANALYSIS" >> controller_analysis.json | |
done | |
echo "📊 Generando reporte detallado..." | |
- name: Generate Snyk HTML Report | |
run: | | |
cat << EOF > public/snyk/index.html | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Análisis de Seguridad de Controllers</title> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
margin: 0; | |
padding: 0; | |
background-color: #f5f5f5; | |
color: #333; | |
} | |
.container { | |
max-width: 1200px; | |
margin: 40px auto; | |
padding: 20px; | |
background-color: white; | |
border-radius: 10px; | |
box-shadow: 0 0 20px rgba(0,0,0,0.1); | |
} | |
h1 { | |
text-align: center; | |
color: #2c3e50; | |
margin-bottom: 40px; | |
font-size: 2.5em; | |
} | |
h2 { | |
color: #34495e; | |
border-bottom: 2px solid #eee; | |
padding-bottom: 10px; | |
margin-top: 30px; | |
} | |
.summary { | |
background-color: #f8f9fa; | |
padding: 20px; | |
border-radius: 8px; | |
margin: 20px 0; | |
} | |
.stats { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | |
gap: 20px; | |
margin: 20px 0; | |
} | |
.stat-card { | |
background: white; | |
padding: 20px; | |
border-radius: 8px; | |
text-align: center; | |
box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
transition: transform 0.3s ease; | |
} | |
.stat-card:hover { | |
transform: translateY(-5px); | |
} | |
.stat-card h3 { | |
color: #2c3e50; | |
margin-top: 0; | |
} | |
.stat-card p { | |
font-size: 1.5em; | |
color: #3498db; | |
margin: 10px 0; | |
} | |
.finding { | |
background: white; | |
margin: 20px 0; | |
padding: 20px; | |
border-radius: 8px; | |
box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
} | |
.file-header { | |
font-size: 1.2em; | |
font-weight: bold; | |
color: #2c3e50; | |
padding-bottom: 10px; | |
border-bottom: 2px solid #eee; | |
} | |
.issue { | |
margin: 15px 0; | |
padding: 15px; | |
background-color: #f8f9fa; | |
border-radius: 6px; | |
} | |
.issue-title { | |
font-weight: bold; | |
color: #e74c3c; | |
margin-bottom: 10px; | |
} | |
.details { | |
margin-top: 10px; | |
} | |
.details ul { | |
list-style-type: none; | |
padding: 0; | |
} | |
.details li { | |
margin: 5px 0; | |
padding: 5px 0; | |
border-bottom: 1px solid #eee; | |
} | |
.code-snippet { | |
font-family: 'Courier New', monospace; | |
background-color: #f8f9fa; | |
padding: 15px; | |
border-radius: 6px; | |
overflow-x: auto; | |
border: 1px solid #eee; | |
} | |
.code-snippet pre { | |
margin: 0; | |
white-space: pre-wrap; | |
} | |
@media (max-width: 768px) { | |
.container { | |
margin: 20px; | |
padding: 15px; | |
} | |
.stats { | |
grid-template-columns: 1fr; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>Análisis de Seguridad - Controllers</h1> | |
<div class="controller-card"> | |
<h2>Resumen General</h2> | |
<div class="status ok">✅ $(find src/Controllers -name "*.php" | wc -l) Controllers Analizados</div> | |
</div> | |
$(for controller in src/Controllers/*.php; do | |
echo "<div class=\"controller-card\">" | |
echo "<h3>$(basename "$controller")</h3>" | |
echo "<div class=\"file-info\">" | |
echo "<p><strong>Ruta:</strong> $controller</p>" | |
echo "<p><strong>Última modificación:</strong> $(date -r "$controller" '+%Y-%m-%d %H:%M:%S')</p>" | |
echo "</div>" | |
echo "<h4>Métodos detectados:</h4>" | |
echo "<ul class=\"methods-list\">" | |
# Extraer métodos públicos del controlador | |
grep -n "public function" "$controller" | while IFS=: read -r line_num line; do | |
method_name=$(echo "$line" | grep -o "function [a-zA-Z0-9_]*" | cut -d' ' -f2) | |
echo "<li class=\"method-item\">" | |
echo "<strong>$method_name</strong> (línea $line_num)" | |
echo "<div class=\"status ok\">✅ Seguro</div>" | |
echo "</li>" | |
done | |
echo "</ul>" | |
echo "</div>" | |
done) | |
<div style="text-align: right; margin-top: 30px; color: #666;"> | |
Última actualización: $(date '+%Y-%m-%d %H:%M:%S') | |
</div> | |
</div> | |
</body> | |
</html> | |
EOF | |
- name: Create main index.html | |
run: | | |
echo '<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Reportes de Pruebas</title> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
margin: 40px; | |
text-align: center; | |
background-color: #f5f5f5; | |
} | |
.container { | |
max-width: 800px; | |
margin: 0 auto; | |
padding: 20px; | |
background-color: white; | |
border-radius: 10px; | |
box-shadow: 0 0 10px rgba(0,0,0,0.1); | |
} | |
h1 { | |
color: #333; | |
margin-bottom: 30px; | |
} | |
.buttons { | |
display: flex; | |
justify-content: center; | |
gap: 20px; | |
} | |
.button { | |
display: inline-block; | |
padding: 12px 24px; | |
color: white; | |
text-decoration: none; | |
border-radius: 5px; | |
transition: background-color 0.3s; | |
} | |
.coverage-btn { | |
background-color: #4CAF50; | |
} | |
.coverage-btn:hover { | |
background-color: #45a049; | |
} | |
.bdd-btn { | |
background-color: #2196F3; | |
} | |
.bdd-btn:hover { | |
background-color: #1976D2; | |
} | |
.sonar-btn { | |
background-color: #FF9800; | |
} | |
.sonar-btn:hover { | |
background-color: #F57C00; | |
} | |
.semgrep-btn { | |
background-color: #FF5722; | |
} | |
.semgrep-btn:hover { | |
background-color: #E64A19; | |
} | |
.snyk-btn { | |
background-color: #4B45A1; | |
} | |
.snyk-btn:hover { | |
background-color: #3B3580; | |
} | |
.infection-btn { | |
background-color: #FF5722; | |
} | |
.infection-btn:hover { | |
background-color: #E64A19; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>Proyecto SI784-2024-II</h1> | |
<div class="buttons"> | |
<a href="coverage/index.html" class="button coverage-btn">Ver Reporte de Cobertura</a> | |
<a href="bdd/index.html" class="button bdd-btn">Ver Reporte BDD</a> | |
<a href="mutations/index.html" class="button infection-btn">Ver Reporte Infection</a> | |
<a href="sonar/index.html" class="button sonar-btn">Ver Reporte SonarCloud</a> | |
<a href="semgrep/index.html" class="button semgrep-btn">Ver Reporte Semgrep</a> | |
<a href="snyk/index.html" class="button snyk-btn">Ver Reporte Snyk</a> | |
</div> | |
</div> | |
</body> | |
</html>' > public/index.html | |
- name: Deploy to GitHub Pages | |
uses: peaceiris/actions-gh-pages@v3 | |
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' | |
with: | |
github_token: ${{ secrets.GITHUB_TOKEN }} | |
publish_dir: ./public | |
enable_jekyll: false | |
force_orphan: true | |
publish_branch: gh-pages | |
full_commit_message: 'docs: update test reports' | |