Guideline: Tích hợp Scanning API của FSP Core Service vào CI/CD Pipelines
Hướng dẫn này sẽ hướng dẫn bạn cách tích hợp các endpoint quét dịch vụ lõi FSP vào quy trình CI/CD của mình, cho phép tự động quét mã nguồn, quét bí mật và quét bảo mật hình ảnh container trên mỗi lần thay đổi.
Tự động hóa các bước quét bảo mật trong pipeline CI/CD giúp phát hiện lỗ hổng sớm. Hướng dẫn này bao gồm:
POST /api/v1/xplat/fsp-core-service/integration/scan-code
Khởi tạo quét mã nguồn cho một kho lưu trữ và commit cụ thể thuộc nhóm FSEC.
Yêu cầu
Headers
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
Orgid | string | Không | ID tổ chức để xác thực |
Body
Content-Type: application/json
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
team_code | string | Có | Mã của nhóm FSEC |
git_repo_url | string | Có | URL kho lưu trữ Git (ví dụ: GitHub hoặc GitLab) |
access_key | string | Có | Khóa truy cập do FSEC cấp để truy cập kho lưu trữ |
branch | string | Có | Tên nhánh cần quét |
commit | string | Có | SHA của commit trên nhánh cần quét |
Ví dụ
POST /api/v1/xplat/fsp-core-service/integration/scan-code HTTP/1.1
Host: api.yourdomain.com
Orgid: 123e4567-e89b-12d3-a456-426614174000
Accept: application/json
Content-Type: application/json
{
"team_code": "FSEC_TEAM_001",
"git_repo_url": "https://github.com/example/repo.git",
"access_key": "abcd1234",
"branch": "main",
"commit": "a1b2c3d4e5f6g7h8i9j0"
}
Phản hồi
{
"data": {
"errorCode": "F-000",
"errorMessage": "",
"data": {
"request_code": "",
"request_time": "2025-03-25 13:34:11"
}
}
}
{
"detail": [
{
"loc": ["string", 0],
"msg": "string",
"type": "string"
}
]
}
POST /api/v1/xplat/fsp-core-service/integration/scan-secret
Khởi tạo quét secret cho một kho lưu trữ và commit cụ thể thuộc nhóm FSEC.
Yêu cầu
Headers
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
Orgid | string | Không | ID tổ chức để xác thực |
Body
Content-Type: application/json
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
team_code | string | Có | Mã của nhóm FSEC |
git_repo_url | string | Có | URL kho lưu trữ Git (ví dụ: GitHub hoặc GitLab) |
access_key | string | Có | Khóa truy cập do FSEC cấp để truy cập kho lưu trữ |
branch | string | Có | Tên nhánh cần quét |
commit | string | Có | SHA của commit trên nhánh cần quét |
Ví dụ
POST /api/v1/xplat/fsp-core-service/integration/scan-secret HTTP/1.1
Host: api.yourdomain.com
Orgid: 123e4567-e89b-12d3-a456-426614174000
Accept: application/json
Content-Type: application/json
{
"team_code": "FSEC_TEAM_001",
"git_repo_url": "https://github.com/example/repo.git",
"access_key": "abcd1234",
"branch": "main",
"commit": "a1b2c3d4e5f6g7h8i9j0"
}
Phản hồi
{
"data": {
"errorCode": "F-000",
"errorMessage": "",
"data": {
"request_code": "",
"request_time": "2025-03-25 13:34:11"
}
}
}
{
"detail": [
{
"loc": ["string", 0],
"msg": "string",
"type": "string"
}
]
}
POST /api/v1/xplat/fsp-core-service/integration/scan-image
Khởi tạo quét image cho một kho lưu trữ và commit cụ thể thuộc nhóm FSEC.
Yêu cầu
Headers
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
Orgid | string | Không | ID tổ chức để xác thực |
Body
Content-Type: application/json
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
team_code | string | Có | Mã của nhóm FSEC |
access_key | string | Có | Khóa truy cập do FSEC cấp cho registry image |
image_url | string | Có | Đường dẫn registry và tag của image (ví dụ: repo:tag) |
Ví dụ
POST /api/v1/xplat/fsp-core-service/integration/scan-image HTTP/1.1
Host: api.yourdomain.com
Orgid: 123e4567-e89b-12d3-a456-426614174000
Accept: application/json
Content-Type: application/json
{
"team_code": "FSEC_TEAM_001",
"access_key": "abcd1234",
"image_url": "registry.example.com/myapp:latest"
}
Phản hồi
{
"data": {
"errorCode": "F-000",
"errorMessage": "",
"data": {
"request_code": "",
"request_time": "2025-03-25 13:34:11"
}
}
}
{
"detail": [
{
"loc": ["string", 0],
"msg": "string",
"type": "string"
}
]
}
POST /api/v1/xplat/fsp-core-service/integration/get-scan-code-result
Lấy kết quả của một lần quét mã nguồn đã khởi tạo trước đó bằng request_code.
Yêu cầu
Headers
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
Orgid | string | Không | ID tổ chức để xác thực |
Body
Content-Type: application/json
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
request_code | string | Có | Mã yêu cầu trả về từ /scan-code |
access_key | string | Có | Khóa truy cập do FSEC cấp |
Ví dụ
POST /api/v1/xplat/fsp-core-service/integration/get-scan-code-result HTTP/1.1
Host: api.yourdomain.com
Orgid: 123e4567-e89b-12d3-a456-426614174000
Accept: application/json
Content-Type: application/json
{
"request_code": "RSC-123456",
"access_key": "abcd1234"
}
Phản hồi
{
"data": {
"errorCode": "F-000",
"errorMessage": "",
"data": {
"request_code": "RSC-123456",
"scan_status": "COMPLETED",
"scan_result": {
"L": 0,
"C": 0,
"M": 4,
"H": 0
},
"finish_time": "2025-03-25 13:34:11",
"quality_gate": "OK"
}
}
}
{
"detail": [
{
"loc": ["string", 0],
"msg": "string",
"type": "string"
}
]
}
POST /api/v1/xplat/fsp-core-service/integration/get-scan-secret-result
Lấy kết quả của một lần quét secret đã khởi tạo trước đó bằng request_code.
Yêu cầu
Headers
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
Orgid | string | Không | ID tổ chức để xác thực |
Body
Content-Type: application/json
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
request_code | string | Có | Mã yêu cầu trả về từ /scan-secret |
access_key | string | Có | Khóa truy cập do FSEC cấp |
Ví dụ
POST /api/v1/xplat/fsp-core-service/integration/get-scan-secret-result HTTP/1.1
Host: api.yourdomain.com
Orgid: 123e4567-e89b-12d3-a456-426614174000
Accept: application/json
Content-Type: application/json
{
"request_code": "RSC-123456",
"access_key": "abcd1234"
}
Phản hồi
{
"data": {
"errorCode": "F-000",
"errorMessage": "",
"data": {
"request_code": "RSC-123456",
"scan_status": "COMPLETED",
"scan_result": {
"L": 0,
"C": 0,
"M": 4,
"H": 0
},
"finish_time": "2025-03-25 13:34:11",
"quality_gate": "OK"
}
}
}
{
"detail": [
{
"loc": ["string", 0],
"msg": "string",
"type": "string"
}
]
}
POST /api/v1/xplat/fsp-core-service/integration/get-scan-image-result
Lấy kết quả của một lần quét image đã khởi tạo trước đó bằng request_code.
Yêu cầu
Headers
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
Orgid | string | Không | ID tổ chức để xác thực |
Body
Content-Type: application/json
Tên | Loại | Bắt buộc | Mô tả |
---|---|---|---|
request_code | string | Có | Mã yêu cầu trả về từ /scan-image |
access_key | string | Có | Khóa truy cập do FSEC cấp |
Ví dụ
POST /api/v1/xplat/fsp-core-service/integration/get-scan-image-result HTTP/1.1
Host: api.yourdomain.com
Orgid: 123e4567-e89b-12d3-a456-426614174000
Accept: application/json
Content-Type: application/json
{
"request_code": "RSC-123456",
"access_key": "abcd1234"
}
Phản hồi
{
"data": {
"errorCode": "F-000",
"errorMessage": "",
"data": {
"request_code": "RSC-123456",
"scan_status": "COMPLETED",
"scan_result": {
"L": 0,
"C": 0,
"M": 4,
"H": 0
},
"finish_time": "2025-03-25 13:34:11",
"quality_gate": "OK"
}
}
}
{
"detail": [
{
"loc": ["string", 0],
"msg": "string",
"type": "string"
}
]
}
POST /api/v1/xplat/fsp-core-service/integration/scan-code
với thông tin chi tiết về repository.POST /api/v1/xplat/fsp-core-service/integration/get-scan-code-result
và lặp lại đến khi status = SUCCEEDED.POST /api/v1/xplat/fsp-core-service/integration/scan-secret
với payload giống bước scan code.POST /api/v1/xplat/fsp-core-service/integration/get-scan-secret-result
và lặp lại đến khi status = SUCCEEDED.POST /api/v1/xplat/fsp-core-service/integration/scan-image
với tag của image.POST /api/v1/xplat/fsp-core-service/integration/get-scan-secret-result
và lặp lại đến khi status = SUCCEEDED.variables:
ORGID: "$FSP_ORGID"
ACCESS_KEY: "$FSP_ACCESS_KEY"
API_URL: "https://api.yourdomain.com/api/v1/xplat/fsp-core-service"
TEAM_CODE: "FSEC_TEAM_001"
stages:
- code-scan
- secret-scan
- build
- image-scan
# Shared script for code & secret scans
.scan-stage: &scan-stage
image: alpine:latest
before_script:
- apk add --no-cache curl jq
script:
- echo "Trigger scan"
- RESPONSE=$(curl -s -X POST "$API_URL/integration/${ENDPOINT}" \
-H "Orgid: $ORGID" \
-H "access_key: $ACCESS_KEY" \
-H "Content-Type: application/json" \
-d "{\"team_code\":\"$TEAM_CODE\",\"git_repo_url\":\"$CI_PROJECT_URL.git\",\"branch\":\"$CI_COMMIT_REF_NAME\",\"commit\":\"$CI_COMMIT_SHA\"}")
- CODE=$(echo "$RESPONSE" | jq -r '.data.data.request_code')
- |
echo "Polling for scan result..."
until [ "$(curl -s -X POST "$API_URL/integration/$RESULT_ENDPOINT" \
-H "Orgid: $ORGID" -H "access_key: $ACCESS_KEY" \
-H "Content-Type: application/json" \
-d "{\"request_code\":\"$CODE\",\"access_key\":\"$ACCESS_KEY\"}" \
| jq -r '.data.data.status')" = "COMPLETED" ]; do
sleep 10
done
- ISSUES=$(curl -s -X POST "$API_URL/integration/$RESULT_ENDPOINT" \
-H "Orgid: $ORGID" -H "access_key: $ACCESS_KEY" \
-H "Content-Type: application/json" \
-d "{\"request_code\":\"$CODE\",\"access_key\":\"$ACCESS_KEY\"}" \
| jq '.data.data.issues_found')
- if [ "$ISSUES" -gt 0 ]; then echo "Found $ISSUES issues in $ENDPOINT"; exit 1; fi
code-scan:
stage: code-scan
variables:
ENDPOINT: "scan-code"
RESULT_ENDPOINT: "get-scan-code-result"
<<: *scan-stage
secret-scan:
stage: secret-scan
variables:
ENDPOINT: "scan-secret"
RESULT_ENDPOINT: "get-scan-secret-result"
<<: *scan-stage
build:
stage: build
script:
- docker build -t myapp:$CI_COMMIT_SHA .
image-scan:
stage: image-scan
script:
- echo "Trigger image scan"
- RESPONSE=$(curl -s -X POST "$API_URL/integration/scan-image" \
-H "Orgid: $ORGID" \
-H "access_key: $ACCESS_KEY" \
-H "Content-Type: application/json" \
-d "{\"team_code\":\"$TEAM_CODE\",\"access_key\":\"$ACCESS_KEY\",\"image_url\":\"registry.example.com/myapp:$CI_COMMIT_SHA\"}")
- CODE=$(echo "$RESPONSE" | jq -r '.data.data.request_code')
- |
echo "Polling for image scan result..."
until [ "$(curl -s -X POST "$API_URL/integration/get-scan-image-result" \
-H "Orgid: $ORGID" -H "access_key: $ACCESS_KEY" \
-H "Content-Type: application/json" \
-d "{\"request_code\":\"$CODE\",\"access_key\":\"$ACCESS_KEY\"}" \
| jq -r '.data.data.scan_status')" = "COMPLETED" ]; do
sleep 10
done
- HIGH=$(curl -s -X POST "$API_URL/integration/get-scan-image-result" \
-H "Orgid: $ORGID" -H "access_key: $ACCESS_KEY" \
-H "Content-Type: application/json" \
-d "{\"request_code\":\"$CODE\",\"access_key\":\"$ACCESS_KEY\"}" \
| jq '.data.data.scan_result.H')
- if [ "$HIGH" -gt 0 ]; then echo "High severity issues found"; exit 1; fi
pipeline {
agent any
environment {
ORGID = credentials('FSP_ORGID')
ACCESS_KEY = credentials('FSP_ACCESS_KEY')
API_URL = 'https://api.yourdomain.com/api/v1/xplat/fsp-core-service'
}
stages {
stage('Code Scan') {
steps {
script {
def payload = [team_code: 'FSEC_TEAM_001', git_repo_url: env.GIT_URL, branch: env.BRANCH_NAME, commit: env.GIT_COMMIT]
def response = httpRequest acceptType: 'APPLICATION_JSON', contentType: 'APPLICATION_JSON', httpMode: 'POST', requestBody: groovy.json.JsonOutput.toJson(payload), url: "${API_URL}/integration/scan-code", customHeaders: [[name:'Orgid', value:ORGID], [name:'access_key', value:ACCESS_KEY]]
def code = readJSON(text: response.content).data.data.request_code
timeout(time: 5, unit: 'MINUTES') {
waitUntil {
def result = httpRequest acceptType: 'APPLICATION_JSON', contentType: 'APPLICATION_JSON', httpMode: 'POST', requestBody: groovy.json.JsonOutput.toJson([request_code: code, access_key: ACCESS_KEY]), url: "${API_URL}/integration/get-scan-code-result", customHeaders: [[name:'Orgid', value:ORGID], [name:'access_key', value:ACCESS_KEY]]
return readJSON(text: result.content).data.data.status == 'COMPLETED'
}
}
def issues = readJSON(text: result.content).data.data.issues_found
if (issues > 0) { error "Found ${issues} code issues" }
}
}
}
stage('Secret Scan') {
steps {
script {
def payload = [team_code: 'FSEC_TEAM_001', git_repo_url: env.GIT_URL, branch: env.BRANCH_NAME, commit: env.GIT_COMMIT]
def response = httpRequest acceptType: 'APPLICATION_JSON', contentType: 'APPLICATION_JSON', httpMode: 'POST', requestBody: groovy.json.JsonOutput.toJson(payload), url: "${API_URL}/integration/scan-secret", custom