Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f87436d
refactor(deps): Actualizacion de dependencias de Node.js
Juan1202 Dec 15, 2025
d0ce300
fix: corregir pruebas unitarias y dependencias
Juan1202 Dec 16, 2025
e553037
actualizaciones
Juan1202 Dec 17, 2025
732cf7b
actualizacion jenkinsfile
Juan1202 Dec 17, 2025
363a0db
actualizacion jenkinsfile
Juan1202 Dec 17, 2025
abde357
actualizacion jenkinsfile
Juan1202 Dec 17, 2025
d0f373d
actualizacion jenkinsfile
Juan1202 Dec 17, 2025
e177ad9
actualizacion jenkinsfile dependency check
Juan1202 Dec 18, 2025
b95af18
actualizaciones jenkinsfile dependency check
Juan1202 Dec 18, 2025
cafec1d
actualizaciones jenkinsfile dependency check1
Juan1202 Dec 18, 2025
bc0326e
actualizaciones jenkinsfile dependency check1.1
Juan1202 Dec 18, 2025
2214cfa
actualizaciones jenkinsfile dependency check1.2
Juan1202 Dec 18, 2025
c65522d
actualizaciones jenkinsfile dependency check1.3
Juan1202 Dec 18, 2025
251dc7f
actualizaciones jenkinsfile dependency check1.4
Juan1202 Dec 18, 2025
cf37e25
actualizaciones jenkinsfile owasp zap
Juan1202 Dec 19, 2025
39660de
actualizaciones jenkinsfile owasp zap1
Juan1202 Dec 19, 2025
5010144
actualizaciones jenkinsfile owasp zap1.1
Juan1202 Dec 19, 2025
ee53b15
actualizaciones jenkinsfile owasp zap1.2
Juan1202 Dec 19, 2025
701b8cb
actualizaciones jenkinsfile owasp zap1.3
Juan1202 Dec 19, 2025
bc2f9f9
actualizaciones jenkinsfile owasp zap1.4
Juan1202 Dec 19, 2025
9d9cd6a
actualizaciones jenkinsfile owasp zap1.5
Juan1202 Dec 19, 2025
5dd428b
actualizaciones jenkinsfile owasp zap1.6
Juan1202 Dec 19, 2025
bafe85e
actualizaciones jenkinsfile owasp zap1.7
Juan1202 Dec 19, 2025
2677e8b
actualizaciones jenkinsfile secrets
Juan1202 Dec 22, 2025
b738778
actualizaciones jenkinsfile dependencycheck1
Juan1202 Dec 22, 2025
2f0617b
actualizaciones vversion sonarqube
Juan1202 Dec 26, 2025
fb0f8d9
actualizaciones jenkinsfile
Juan1202 Dec 26, 2025
8e91d0a
actualizaciones jenkinsfile1
Juan1202 Dec 26, 2025
9931593
actualizaciones jenkinsfile2
Juan1202 Dec 26, 2025
12b7450
actualizaciones jenkinsfile IaC
Juan1202 Dec 29, 2025
93b565f
actualizaciones dockercompose
Juan1202 Dec 29, 2025
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
271 changes: 189 additions & 82 deletions express-mysql/Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,94 +1,201 @@
// Function to validate that the message returned from SonarQube is ok
def qualityGateValidation(qg) {
if (qg.status != 'OK') {
// emailext body: "WARNING SANTI: Code coverage is lower than 80% in Pipeline ${BUILD_NUMBER}", subject: 'Error Sonar Scan, Quality Gate', to: "${EMAIL_ADDRESS}"
return true
}
// emailext body: "CONGRATS SANTI: Code coverage is higher than 80% in Pipeline ${BUILD_NUMBER} - SUCCESS", subject: 'Info - Correct Pipeline', to: "${EMAIL_ADDRESS}"
return false
}
pipeline {
agent any
agent any

tools {
nodejs 'nodejs'
}
tools {
nodejs 'nodejs'
}

environment {
// General Variables for Pipeline
PROJECT_ROOT = 'express-mysql/app'
EMAIL_ADDRESS = 'san99tiagodevsecops@gmail.com'
REGISTRY = 'san99tiago/docker-pirate-express'
}
environment {
PROJECT_ROOT = 'express-mysql/app'
REGISTRY = 'juan1202/docker-pirate-express'
SONAR_CRED_ID = 'sonar-token'
DOCKER_CRED_ID = 'dockerhub-creds'
}

stages {
stage('Hello') {
steps {
// First stage is a sample hello-world step to verify correct Jenkins Pipeline
echo 'Hello World, I am Happy'
echo 'This is my amazing Pipeline'
stages {
stage('Initialize') {
steps {
echo "Preparando entorno..."
// Limpiamos reportes anteriores para evitar confusiones
sh "rm -f zap_report.html kics-report.html dependency-check-report.xml"
sh "chmod -R 777 ."
}
}

stage('Checkout') {
steps {
checkout([$class: 'GitSCM',
branches: [[name: '*/feature/configuracion-inicial-jenkins']],
extensions: [[$class: 'CloneOption', depth: 1, shallow: true, timeout: 30]],
userRemoteConfigs: [[url: 'https://github.com/Juan1202/docker-pirate.git']]
])
}
}

stage('Secret Scanning (TruffleHog)') {
steps {
script {
echo "Buscando secretos en el código..."
sh """
docker run --rm -v \$(pwd):/proj trufflesecurity/trufflehog:latest filesystem /proj --only-verified
"""
}
}
}

// --- NUEVA ETAPA: IaC SCANNING ---
stage('IaC Security (KICS)') {
steps {
script {
echo "Analizando seguridad del Dockerfile e Infraestructura..."
try {
sh """
docker run --rm -u root \
-v \$(pwd):/path \
checkmarx/kics:latest scan \
-p /path \
-o /path \
--report-formats html \
--output-name kics-report \
--ignore-on-exit results
"""
} catch (Exception e) {
echo "KICS encontró problemas de seguridad en la infraestructura."
}
}
publishHTML([
allowMissing: true,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.',
reportFiles: 'kics-report.html',
reportName: 'KICS IaC Report'
])
}
}
// ---------------------------------

stage('Install dependencies') {
steps {
sh "cd ${PROJECT_ROOT} && npm install"
}
}
}
stage('Checkout') {
steps {
// Get Github repo using Github credentials (previously added to Jenkins credentials)
checkout([$class: 'GitSCM', branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[url: 'https://github.com/san99tiago/docker-pirate']]]) }
}
stage('Install dependencies') {
steps {
sh 'npm --version'
sh "cd ${PROJECT_ROOT}; npm install"

stage('Unit tests & Coverage') {
steps {
sh "cd ${PROJECT_ROOT} && npm run test"
sh "cd ${PROJECT_ROOT} && npm run coverage"
}
}
}
stage('Unit tests') {
steps {
// Run unit tests
sh "cd ${PROJECT_ROOT}; npm run test"

stage('SonarQube Scan') {
environment {
SCANNER_HOME = tool 'sonar-scanner'
}
steps {
withCredentials([string(credentialsId: "${SONAR_CRED_ID}", variable: 'SONAR_TOKEN')]) {
withSonarQubeEnv('sonarqube') {
sh """
${SCANNER_HOME}/bin/sonar-scanner \
-Dsonar.projectKey=SimpleExpressExample:Test \
-Dsonar.projectName=SimpleExpressExample \
-Dsonar.projectVersion=0.0.${BUILD_NUMBER} \
-Dsonar.sources=${PROJECT_ROOT}/app.js,${PROJECT_ROOT}/config/db.config.js,${PROJECT_ROOT}/routes/developers.js \
-Dsonar.tests=${PROJECT_ROOT}/test \
-Dsonar.token=${SONAR_TOKEN} \
-Dsonar.javascript.lcov.reportPaths=${PROJECT_ROOT}/coverage/lcov.info
"""
}
}
timeout(time: 2, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
}
stage('Generate coverage report') {
steps {
// Run code-coverage reports
sh "cd ${PROJECT_ROOT}; npm run coverage"

stage('OWASP Dependency-Check') {
steps {
script {
try {
dependencyCheck additionalArguments: """
--scan ${PROJECT_ROOT}
--format HTML
--format XML
--out .
--noupdate
""",
odcInstallation: 'dependency-check'
} catch (Exception e) {
echo "SCA finalizado."
}
}
dependencyCheckPublisher(pattern: 'dependency-check-report.xml', stopBuild: false)
}
}
}
stage('scan') {
environment {
// Previously defined in the Jenkins "Global Tool Configuration"
scannerHome = tool 'sonar-scanner'
}
steps {
// "sonarqube" is the server configured in "Configure System"
withSonarQubeEnv('sonarqube') {
// Execute the SonarQube scanner with desired flags
sh "${scannerHome}/bin/sonar-scanner \
-Dsonar.projectKey=SimpleExpressExample:Test \
-Dsonar.projectName=SimpleExpressExample \
-Dsonar.projectVersion=0.0.${BUILD_NUMBER} \
-Dsonar.host.url=http://mysonarqube:9000 \
-Dsonar.sources=./${PROJECT_ROOT}/app.js,./${PROJECT_ROOT}/config/db.config.js,./${PROJECT_ROOT}/routes/developers.js \
-Dsonar.login=admin \
-Dsonar.password=admin \
-Dsonar.tests=./${PROJECT_ROOT}/test \
-Dsonar.javascript.lcov.reportPaths=./${PROJECT_ROOT}/coverage/lcov.info"

stage('Build docker-image') {
steps {
sh "cd ${PROJECT_ROOT} && docker build -t ${REGISTRY}:${BUILD_NUMBER} ."
}
}

stage('OWASP ZAP Scan (DAST)') {
steps {
script {
// Limpieza preventiva para evitar conflicto de nombres
sh "docker rm -f zap-test-app || true"

sh "docker run -d --name zap-test-app -p 3001:3000 ${REGISTRY}:${BUILD_NUMBER}"
sleep 25

try {
def appIp = sh(script: "docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' zap-test-app", returnStdout: true).trim()

sh """
docker run --rm --name zap-container \
-u root \
-v /var/jenkins_home/workspace/pipeline_juan:/zap/wrk/:rw \
ghcr.io/zaproxy/zaproxy:stable zap-baseline.py \
-t http://${appIp}:3000 \
-r zap_report.html
"""
} catch (Exception e) {
echo "ZAP finalizó con hallazgos."
} finally {
sh "docker stop zap-test-app && docker rm zap-test-app"

sh "find . -name 'zap_report.html' -exec cp {} . \\;"
sh "chmod 777 zap_report.html || true"
sh "chmod 777 kics-report.html || true" // Asegurar permisos para el reporte KICS

publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.',
reportFiles: 'zap_report.html',
reportName: 'OWASP ZAP Report'
])
}
}
}
timeout(time: 3, unit: 'MINUTES') {
// In case of SonarQube failure or direct timeout exceed, stop Pipeline
waitForQualityGate abortPipeline: qualityGateValidation(waitForQualityGate())
}

stage('Deploy docker-image') {
steps {
withCredentials([usernamePassword(credentialsId: "${DOCKER_CRED_ID}", passwordVariable: 'DOCKER_PWD', usernameVariable: 'DOCKER_USER')]) {
sh "echo \$DOCKER_PWD | docker login -u \$DOCKER_USER --password-stdin"
sh "docker push ${REGISTRY}:${BUILD_NUMBER}"
}
}
}
}
stage('Build docker-image') {
steps {
sh "cd ./${PROJECT_ROOT};docker build -t ${REGISTRY}:${BUILD_NUMBER} . "
}
}
stage('Deploy docker-image') {
steps {
// If the Dockerhub authentication stopped, do it again
sh 'docker login'
sh "docker push ${REGISTRY}:${BUILD_NUMBER}"
}

post {
always {
// Limpieza final de seguridad: Borramos contenedores huérfanos si algo falló
sh "docker rm -f zap-test-app || true"
echo "Pipeline finalizado. Revisa los reportes de KICS, Sonar, Dependency-Check y ZAP."
}
}
}
}
}
}
31 changes: 20 additions & 11 deletions express-mysql/app/app.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
// express-mysql/app/app.js (CÓDIGO CORREGIDO)
const express = require("express");
const morgan = require("morgan");

const developerRoutes = require("./routes/developers");
const app = express();
const router = require("./routes/developers.js");

const PORT = process.env.APP_PORT || 4000;

app.disable("etag"); // Disable cache 304 status-code
app.use(morgan("short")); // Log server requests
app.use(express.json()); // Parse JSON bodies (legacy was body-parser)
app.use(router); // Expand routes functionalities with custom router
app.use(express.json()); // Necesario para POST/PUT
app.use(morgan("tiny"));
app.use("/", developerRoutes);

// EXTRA COMMENT
// Manejador de ruta por defecto
app.get("/", (req, res) => {
res.status(200).json({ message: "Hello, My name is Santiago." });
  res.status(200).json({ message: "Welcome to the REST API example!" });
});

app.listen(PORT, () => console.log(`Listening on port ${PORT}`));
// Middleware para manejar rutas 404
app.use((req, res) => {
  res.status(404).json({ message: "404 Not Found" });
});

// Exporta la aplicación Express, pero NO la inicia
module.exports = app;

// Solo inicia el servidor si el archivo se ejecuta directamente (no por los tests)
if (require.main === module) {
  const port = process.env.PORT || 3000;
  app.listen(port, () => {
    console.log(`Listening on port ${port}`);
  });
}
34 changes: 21 additions & 13 deletions express-mysql/app/config/db.config.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
"use strict";
const mysql = require("mysql");

// Connect to MySQL based on environment variables or default
const dbConn = mysql.createConnection({
host: process.env.DB_HOST || "localhost",
port: process.env.DB_PORT || 3306,
user: "root",
password: "root",
database: "employees",
});

module.exports = dbConn;
// config/db.config.js (Código CORREGIDO y Actualizado)

// 1. IMPORTAR mysql2/promise (con soporte para async/await)
const mysql = require('mysql2/promise');

// 2. Definir la configuración de la conexión (asegúrate de que los nombres de los archivos .json sean correctos)
const dbConfiguration = {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || 'root',
database: process.env.DB_DATABASE || 'employees',
port: process.env.DB_PORT || 3306,
waitForConnections: true,
connectionLimit: 10, // Usar Pool para eficiencia
queueLimit: 0,
};

// 3. Crear el Pool de conexiones y exportarlo
const dbPool = mysql.createPool(dbConfiguration);

module.exports = dbPool;
Loading