Skip to content
Open
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
92 changes: 92 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: DevSecOps Baseline CI
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
# 1. CẤU HÌNH TRIGGER: Khi nào thì pipeline này được chạy?
on:
push:
branches: [ "main", "dev", "feature/docker_build" ] # Chạy khi có code push lên nhánh main hoặc dev
pull_request:
branches: [ "main" ] # Chạy khi có ai đó tạo Pull Request đòi gộp code vào main

# 2. CẤU HÌNH JOBS: Các công việc cần làm
jobs:
# --- KIỂM TRA HẠ TẦNG ---
terraform-check:
name: 1. Terraform Validation
runs-on: ubuntu-latest
steps:
- name: 1. Checkout Source Code
uses: actions/checkout@v4

- name: 2. Setup Terraform
uses: hashicorp/setup-terraform@v3

- name: 3. Terraform Init & Validate
run: |
cd terraform
terraform init
terraform validate

# # --- QUÉT MÃ NGUỒN BẢO MẬT (SAST) ---
sast-scan:
name: 2. SAST Scanning (Semgrep)
needs: terraform-check
runs-on: ubuntu-latest
steps:
- name: 1. Checkout Source Code
uses: actions/checkout@v4

- name: 2. Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/nodejs

# --- DOCKER BUILD ---
build-and-test:
name: Build & Test Application
# needs: sast-scan
runs-on: ubuntu-latest # Mượn một máy ảo Ubuntu miễn phí của GitHub để chạy

steps:
# Bước 1: Lấy code từ GitHub về cái máy ảo Ubuntu kia
- name: 1. Checkout Source Code
uses: actions/checkout@v4

# Bước 2: Setup môi trường
# --- Dành cho NodeJS ---
- name: 2. Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'

# # --- Dành cho Python ---
# - name: 2. Setup Python
# uses: actions/setup-python@v4
# with:
# python-version: '3.10'

# Bước 3: Build thử Docker Image
- name: 3. Docker build image (No Push)
#pattern: ocker build -t ghcr.io/<repo_name>:<commit-sha> thay repo_name thành tên mong muốn
run: docker build -t nt524:${{ github.sha }} .

# Bước 4: Tạo SBOM với Syft
- name: 4. Generate SBOM with Syft
#install syft and using Syft to generate SBOM
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
syft nt524:${{ github.sha }} -o spdx-json > sbom.spdx.json

# Bước 5: Quét Image với Trivy
- name: Run Trivy Vulnerability Scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'nt524:${{ github.sha }}'
format: 'table'
exit-code: '1' # Nếu Trivy phát hiện lỗ hỏng mức HIGH hoặc CRITICAl, nó sẽ trả về mã lỗi 1
ignore-unfixed: true # Báo các lỗ hỏng đã có bản vá
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'

4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ test/e2e/videos/

# ignore Snyk Code scanner files
.dccache

*.terraform
*.terraform.lock.hcl
*terraform.tfstate*
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ addons:
packages:
- libgconf-2-4


## Cache NPM folder and Cypress binary
## to avoid downloading Cypress again and again
cache:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Other settings can be changed by updating the [config file](https://github.com/O

The repo includes the Dockerfile and docker-compose.yml necessary to set up the app and db instance, then connect them together.

1) Install [docker](https://docs.docker.com/installation/) and [docker compose](https://docs.docker.com/compose/install/)
1) Install [docker](https://docs.docker.com/installation/) and [docker compose](https://docs.docker.com/compose/install/)

2) Clone the github repository:
```
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions app/data/allocations-dao.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ const AllocationsDAO = function(db){
// to inject arbitrary javascript code into the NoSQL query:
// 1. 0';while(true){}'
// 2. 1'; return 1 == '1
// Also implement fix in allocations.html for UX.
// Also implement fix in allocations.html for UX.
const parsedThreshold = parseInt(threshold, 10);

if (parsedThreshold >= 0 && parsedThreshold <= 99) {
return {$where: `this.userId == ${parsedUserId} && this.stocks > ${parsedThreshold}`};
}
Expand Down
2 changes: 1 addition & 1 deletion app/routes/profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function ProfileHandler(db) {
// doesn't end up as an XSS attack, the context is incorrect as it is encoding the firstname for HTML
// while this same variable is also used in the context of a URL link element
doc.website = ESAPI.encoder().encodeForHTML(doc.website);
// fix it by replacing the above with another template variable that is used for
// fix it by replacing the above with another template variable that is used for
// the context of a URL in a link header
// doc.website = ESAPI.encoder().encodeForURL(doc.website)

Expand Down
2 changes: 1 addition & 1 deletion app/views/allocations.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ <h3 class="panel-title">
<div class="form-group">
<!--Fix for A1 - 2 NoSQL Injection - Provide validation for input.
Adhering to defence in depth, on the front-end mostly for UX.
The attacker, or user should not be able to enter anything other than 0-99.
The attacker, or user should not be able to enter anything other than 0-99.
Also implement fix in allocations-dao.js-->
<!--<input type="number" min="0" max="99" class="form-control" placeholder="Stocks Threshold" name="threshold" />-->
<input type="text" class="form-control" placeholder="Stocks Threshold" name="threshold" />
Expand Down
18 changes: 9 additions & 9 deletions app/views/tutorial/a2.html
Original file line number Diff line number Diff line change
Expand Up @@ -236,15 +236,15 @@ <h3 class="panel-title">Description</h3>
Implementing a robust minimum password criteria (minimum length and complexity) can make it difficult for attacker to guess password.
</div>
</div>
<!--
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Attack Scenario Demo</h3>
</div>
<div class="panel-body">
Screencast showing how attack can manifest in the target application ...
</div>
</div>
<!--
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Attack Scenario Demo</h3>
</div>
<div class="panel-body">
Screencast showing how attack can manifest in the target application ...
</div>
</div>
-->
<div class="panel panel-default">
<div class="panel-heading">
Expand Down
4 changes: 2 additions & 2 deletions app/views/tutorial/a5.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ <h3 class="panel-title">Source Code Example</h3>
</div>
<p>The default HTTP header x-powered-by can reveal implementation details to an attacker. It can be taken out by including this code in
<code>server.js</code>
<pre>
app.disable("x-powered-by");
<pre>
app.disable("x-powered-by");
</pre>
</p>
<p>The default session cookie name for express sessions can be changed by setting key attribute while creating express session.
Expand Down
6 changes: 3 additions & 3 deletions app/views/tutorial/a8.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ <h3 class="panel-title">Source Code Example</h3>
//Enable Express csrf protection
app.use(express.csrf());

app.use(function(req, res, next) {
res.locals.csrftoken = req.csrfToken();
next();
app.use(function(req, res, next) {
res.locals.csrftoken = req.csrfToken();
next();
}); </pre> Next, this token can be included in a hidden form field in
<code>views/profile.html</code>as below.
<pre>
Expand Down
2 changes: 1 addition & 1 deletion app/views/tutorial/ssrf.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ <h3 class="panel-title">Attack Mechanics</h3>
<pre>
// If a stock symbol has been submitted, concatenate the symbol to the URL and return the HTTP Response
if (req.query.symbol) {
var url = req.query.url+req.query.symbol;
var url = req.query.url+req.query.symbol;
needle.get(url, function(error, newResponse) { ... }
</pre> An attacker can change the
<code>url</code> and <code>symbol</code> parameters to point to an attacker-controlled website to interact with the server.
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "3.7"
# version: "3.7"

services:
web:
Expand Down
13 changes: 13 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const securityPlugin = require("eslint-plugin-security");

module.exports = [
// Bật toàn bộ rule bảo mật khuyên dùng
securityPlugin.configs.recommended,
{
// Cấu hình môi trường cho NodeGoat (Node.js)
languageOptions: {
ecmaVersion: "latest",
sourceType: "commonjs"
}
}
];
21 changes: 21 additions & 0 deletions k8s/mongodb_deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb
namespace: nodegoat-staging
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo:4.4
imagePullPolicy: IfNotPresent
ports:
- containerPort: 27017
12 changes: 12 additions & 0 deletions k8s/mongodb_service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: mongodb
namespace: nodegoat-staging
spec:
selector:
app: mongodb
ports:
- protocol: TCP
port: 27017
targetPort: 27017
29 changes: 29 additions & 0 deletions k8s/nodegoat_deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodegoat-app
namespace: nodegoat-staging
spec:
replicas: 1
selector:
matchLabels:
app: nodegoat
template:
metadata:
labels:
app: nodegoat
spec:
containers:
- name: nodegoat
# Thay thế bằng image bạn đã build ở Bước 1
image: ${app_image} #thay bằng tên image vừa build
imagePullPolicy: IfNotPresent
ports:
- containerPort: 4000
env:
# Cấu hình chuỗi kết nối DB trỏ tới Service MongoDB nội bộ
- name: MONGODB_URI
value: "mongodb://mongodb:27017/nodegoat"
command: [ "sh", "-c" ]
args:
- "until nc -z -w 2 mongodb 27017 && echo 'mongodb is ready for connections' && node artifacts/db-reset.js && npm start; do sleep 2; done"
13 changes: 13 additions & 0 deletions k8s/nodegoat_service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: nodegoat-app
namespace: nodegoat-staging
spec:
type: ClusterIP
selector:
app: nodegoat
ports:
- protocol: TCP
port: 4000
targetPort: 4000
11 changes: 11 additions & 0 deletions terraform/Namespace.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Create a Kubernetes namespace for the NodeGoat staging environment
resource "kubernetes_namespace" "nodegoat_staging" {
metadata {
name = "nodegoat-staging"
labels ={
environment = "staging"
app = "nodegoat"
"pod-security.kubernetes.io/enforce" = "baseline"
}
}
}
18 changes: 18 additions & 0 deletions terraform/Network_Policy.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
resource "kubernetes_network_policy" "staging_internal_allow" {
metadata {
name = "allow-internal-staging"
namespace = "nodegoat-staging"
}

spec {
pod_selector {} # Áp dụng cho TẤT CẢ pod trong namespace

ingress {
from {
pod_selector {} # Cho phép traffic đến từ các pod khác CÙNG namespace
}
}

policy_types = ["Ingress"]
}
}
12 changes: 12 additions & 0 deletions terraform/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
terraform {
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.23.0"
}
}
}

provider "kubernetes" {
config_path = "~/.kube/config"
}