fix-semgrep #103
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: | | |
echo "🔍 ANÁLISIS DE SEGURIDAD SEMGREP" | |
echo "==============================" | |
# Ejecutar scan con todos los detalles | |
semgrep scan \ | |
--verbose \ | |
--no-git-ignore \ | |
--max-target-bytes=5000000 \ | |
--no-rewrite-rule-ids \ | |
--metrics=off \ | |
--json | tee semgrep_results.json | |
# Mostrar resumen de resultados | |
echo "📊 Resumen de Resultados:" | |
cat semgrep_results.json | jq '.' | |
- name: Update Semgrep HTML Report | |
run: | | |
mkdir -p public/semgrep | |
echo '<!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; | |
} | |
.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); | |
} | |
.finding { | |
margin: 20px 0; | |
padding: 15px; | |
border: 1px solid #ddd; | |
border-radius: 5px; | |
background-color: #fff; | |
} | |
.file-header { | |
font-size: 1.2em; | |
font-weight: bold; | |
color: #333; | |
margin-bottom: 10px; | |
padding-bottom: 5px; | |
border-bottom: 2px solid #eee; | |
} | |
.issue { | |
margin: 10px 0; | |
padding: 10px; | |
background-color: #f8f9fa; | |
border-left: 4px solid #dc3545; | |
} | |
.issue-title { | |
font-weight: bold; | |
color: #dc3545; | |
} | |
.code-snippet { | |
font-family: monospace; | |
background-color: #f8f9fa; | |
padding: 10px; | |
border-radius: 4px; | |
margin: 10px 0; | |
white-space: pre-wrap; | |
} | |
.details { | |
margin-top: 5px; | |
font-size: 0.9em; | |
color: #666; | |
} | |
.summary { | |
margin: 20px 0; | |
padding: 15px; | |
background-color: #f8f9fa; | |
border-radius: 5px; | |
} | |
.stats { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
gap: 20px; | |
margin: 20px 0; | |
} | |
.stat-card { | |
background: white; | |
padding: 15px; | |
border-radius: 5px; | |
text-align: center; | |
box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
} | |
</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>267 archivos</p> | |
</div> | |
<div class="stat-card"> | |
<h3>Hallazgos Totales</h3> | |
<p>24 problemas</p> | |
</div> | |
<div class="stat-card"> | |
<h3>Reglas Aplicadas</h3> | |
<p>294 reglas</p> | |
</div> | |
</div> | |
</div> | |
<h2>Hallazgos por Archivo</h2> | |
<div class="finding"> | |
<div class="file-header">docker-compose.yml</div> | |
<div class="issue"> | |
<div class="issue-title">Problemas de Seguridad en Servicios Docker</div> | |
<ul> | |
<li>Servicio "db" permite escalación de privilegios</li> | |
<li>Sistema de archivos escribible en servicio "db"</li> | |
<li>Servicio "selenium-hub" permite escalación de privilegios</li> | |
<li>Sistema de archivos escribible en servicio "selenium-hub"</li> | |
<li>Servicio "chrome" permite escalación de privilegios</li> | |
<li>Sistema de archivos escribible en servicio "chrome"</li> | |
</ul> | |
</div> | |
</div> | |
<div class="finding"> | |
<div class="file-header">public/bdd/index.html.html y reports/index.html.html</div> | |
<div class="issue"> | |
<div class="issue-title">Falta de Atributo de Integridad en Recursos Externos</div> | |
<div class="details"> | |
Los siguientes recursos carecen del atributo "integrity": | |
<ul> | |
<li>bootstrap.min.css</li> | |
<li>bootstrap-theme.min.css</li> | |
<li>html5shiv.min.js</li> | |
<li>respond.min.js</li> | |
<li>Chart.min.js</li> | |
<li>jquery.min.js</li> | |
<li>bootstrap.min.js</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<div class="finding"> | |
<div class="file-header">sql/tienda_bd.sql</div> | |
<div class="issue"> | |
<div class="issue-title">Hash bcrypt Detectado</div> | |
<div class="code-snippet"> | |
Líneas 127-128: Hashes bcrypt encontrados en inserciones de usuarios | |
</div> | |
</div> | |
</div> | |
<div class="finding"> | |
<div class="file-header">src/Controllers/AdminController.php</div> | |
<div class="issue"> | |
<div class="issue-title">Uso Inseguro de unlink()</div> | |
<div class="details"> | |
El uso de unlink() con entrada de usuario es potencialmente peligroso. | |
Encontrado en líneas 153 y 227. | |
</div> | |
</div> | |
</div> | |
<div class="summary"> | |
<h2>Notas Adicionales</h2> | |
<ul> | |
<li>4 archivos fueron parcialmente analizados debido a errores de parsing</li> | |
<li>6 archivos mayores a 1.0 MB fueron omitidos</li> | |
<li>45 archivos fueron omitidos por patrones en .semgrepignore</li> | |
</ul> | |
</div> | |
</div> | |
</body> | |
</html>' > public/semgrep/index.html | |
- 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: 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); | |
} | |
.controller-card { | |
background: #f8f9fa; | |
padding: 20px; | |
margin: 20px 0; | |
border-radius: 8px; | |
border: 1px solid #dee2e6; | |
} | |
.file-info { | |
background: #e9ecef; | |
padding: 10px; | |
border-radius: 4px; | |
margin: 10px 0; | |
} | |
.methods-list { | |
list-style: none; | |
padding: 0; | |
} | |
.method-item { | |
background: white; | |
padding: 10px; | |
margin: 5px 0; | |
border-radius: 4px; | |
border: 1px solid #dee2e6; | |
} | |
.status { | |
display: inline-block; | |
padding: 5px 10px; | |
border-radius: 4px; | |
margin: 5px 0; | |
} | |
.status.ok { | |
background-color: #28a745; | |
color: white; | |
} | |
.status.warning { | |
background-color: #ffc107; | |
} | |
.status.error { | |
background-color: #dc3545; | |
color: white; | |
} | |
.code-preview { | |
background: #f8f9fa; | |
padding: 10px; | |
border-radius: 4px; | |
font-family: monospace; | |
white-space: pre-wrap; | |
} | |
</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' | |