generated from nhcarrigan/template
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eecb1b1841 | |||
| 7587152072 | |||
| 73995a495d |
+11
-14
@@ -8,35 +8,32 @@ on:
|
|||||||
- main
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
lint:
|
||||||
name: CI
|
name: Lint and Test
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Source Files
|
- name: Checkout Source Files
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Use Node.js v24
|
- name: Use Node.js v22
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 22
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 9
|
||||||
|
|
||||||
- name: Ensure Dependencies are Pinned
|
|
||||||
uses: naomi-lgbt/dependency-pin-check@main
|
|
||||||
with:
|
|
||||||
language: javascript
|
|
||||||
dev-dependencies: true
|
|
||||||
peer-dependencies: true
|
|
||||||
optional-dependencies: true
|
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Build internal package
|
||||||
|
run: cd packages/types && pnpm build
|
||||||
|
|
||||||
|
- name: Install again
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
- name: Lint Source Files
|
- name: Lint Source Files
|
||||||
run: pnpm run lint
|
run: pnpm run lint
|
||||||
|
|
||||||
|
|||||||
@@ -1,177 +0,0 @@
|
|||||||
name: Security Scan and Upload
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * 1'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
security-audit:
|
|
||||||
name: Security & DefectDojo Upload
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# --- AUTO-SETUP PROJECT ---
|
|
||||||
- name: Ensure DefectDojo Product Exists
|
|
||||||
env:
|
|
||||||
DD_URL: ${{ secrets.DD_URL }}
|
|
||||||
DD_TOKEN: ${{ secrets.DD_TOKEN }}
|
|
||||||
PRODUCT_NAME: ${{ github.repository }}
|
|
||||||
PRODUCT_TYPE_ID: 1
|
|
||||||
run: |
|
|
||||||
sudo apt-get install jq -y > /dev/null
|
|
||||||
|
|
||||||
echo "Checking connection to $DD_URL..."
|
|
||||||
|
|
||||||
# Check if product exists - capture HTTP code to debug connection issues
|
|
||||||
RESPONSE=$(curl --write-out "%{http_code}" --silent --output /tmp/response.json \
|
|
||||||
-H "Authorization: Token $DD_TOKEN" \
|
|
||||||
"$DD_URL/api/v2/products/?name=$PRODUCT_NAME")
|
|
||||||
|
|
||||||
# If response is not 200, print error
|
|
||||||
if [ "$RESPONSE" != "200" ]; then
|
|
||||||
echo "::error::Failed to query DefectDojo. HTTP Code: $RESPONSE"
|
|
||||||
cat /tmp/response.json
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
COUNT=$(cat /tmp/response.json | jq -r '.count')
|
|
||||||
|
|
||||||
if [ "$COUNT" = "0" ]; then
|
|
||||||
echo "Creating product '$PRODUCT_NAME'..."
|
|
||||||
curl -s -X POST "$DD_URL/api/v2/products/" \
|
|
||||||
-H "Authorization: Token $DD_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{ "name": "'"$PRODUCT_NAME"'", "description": "Auto-created by Gitea Actions", "prod_type": '$PRODUCT_TYPE_ID' }'
|
|
||||||
else
|
|
||||||
echo "Product '$PRODUCT_NAME' already exists."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- 1. TRIVY (Dependencies & Misconfig) ---
|
|
||||||
- name: Install Trivy
|
|
||||||
run: |
|
|
||||||
sudo apt-get install wget apt-transport-https gnupg lsb-release -y
|
|
||||||
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
|
|
||||||
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
|
|
||||||
sudo apt-get update && sudo apt-get install trivy -y
|
|
||||||
|
|
||||||
- name: Run Trivy (FS Scan)
|
|
||||||
run: |
|
|
||||||
trivy fs . --scanners vuln,misconfig --format json --output trivy-results.json --exit-code 0
|
|
||||||
|
|
||||||
- name: Upload Trivy to DefectDojo
|
|
||||||
env:
|
|
||||||
DD_URL: ${{ secrets.DD_URL }}
|
|
||||||
DD_TOKEN: ${{ secrets.DD_TOKEN }}
|
|
||||||
run: |
|
|
||||||
echo "Uploading Trivy results..."
|
|
||||||
# Generate today's date in YYYY-MM-DD format
|
|
||||||
TODAY=$(date +%Y-%m-%d)
|
|
||||||
|
|
||||||
HTTP_CODE=$(curl --write-out "%{http_code}" --output response.txt --silent -X POST "$DD_URL/api/v2/import-scan/" \
|
|
||||||
-H "Authorization: Token $DD_TOKEN" \
|
|
||||||
-F "active=true" \
|
|
||||||
-F "verified=true" \
|
|
||||||
-F "scan_type=Trivy Scan" \
|
|
||||||
-F "engagement_name=CI/CD Pipeline" \
|
|
||||||
-F "product_name=${{ github.repository }}" \
|
|
||||||
-F "scan_date=$TODAY" \
|
|
||||||
-F "auto_create_context=true" \
|
|
||||||
-F "file=@trivy-results.json")
|
|
||||||
|
|
||||||
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
|
|
||||||
echo "::error::Upload Failed with HTTP $HTTP_CODE"
|
|
||||||
echo "--- SERVER RESPONSE ---"
|
|
||||||
cat response.txt
|
|
||||||
echo "-----------------------"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "Upload Success!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- 2. GITLEAKS (Secrets) ---
|
|
||||||
- name: Install Gitleaks
|
|
||||||
run: |
|
|
||||||
wget -qO gitleaks.tar.gz https://github.com/gitleaks/gitleaks/releases/download/v8.18.0/gitleaks_8.18.0_linux_x64.tar.gz
|
|
||||||
tar -xzf gitleaks.tar.gz
|
|
||||||
sudo mv gitleaks /usr/local/bin/ && chmod +x /usr/local/bin/gitleaks
|
|
||||||
|
|
||||||
- name: Run Gitleaks
|
|
||||||
run: gitleaks detect --source . -v --report-path gitleaks-results.json --report-format json --no-git || true
|
|
||||||
|
|
||||||
- name: Upload Gitleaks to DefectDojo
|
|
||||||
env:
|
|
||||||
DD_URL: ${{ secrets.DD_URL }}
|
|
||||||
DD_TOKEN: ${{ secrets.DD_TOKEN }}
|
|
||||||
run: |
|
|
||||||
echo "Uploading Gitleaks results..."
|
|
||||||
TODAY=$(date +%Y-%m-%d)
|
|
||||||
|
|
||||||
HTTP_CODE=$(curl --write-out "%{http_code}" --output response.txt --silent -X POST "$DD_URL/api/v2/import-scan/" \
|
|
||||||
-H "Authorization: Token $DD_TOKEN" \
|
|
||||||
-F "active=true" \
|
|
||||||
-F "verified=true" \
|
|
||||||
-F "scan_type=Gitleaks Scan" \
|
|
||||||
-F "engagement_name=CI/CD Pipeline" \
|
|
||||||
-F "product_name=${{ github.repository }}" \
|
|
||||||
-F "scan_date=$TODAY" \
|
|
||||||
-F "auto_create_context=true" \
|
|
||||||
-F "file=@gitleaks-results.json")
|
|
||||||
|
|
||||||
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
|
|
||||||
echo "::error::Upload Failed with HTTP $HTTP_CODE"
|
|
||||||
echo "--- SERVER RESPONSE ---"
|
|
||||||
cat response.txt
|
|
||||||
echo "-----------------------"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "Upload Success!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- 3. SEMGREP (SAST) ---
|
|
||||||
- name: Install Semgrep (via pipx)
|
|
||||||
run: |
|
|
||||||
sudo apt-get install pipx -y
|
|
||||||
pipx install semgrep
|
|
||||||
# Add pipx binary path to GITHUB_PATH so next steps can see 'semgrep'
|
|
||||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
|
||||||
|
|
||||||
- name: Run Semgrep
|
|
||||||
run: semgrep scan --config=p/security-audit --config=p/owasp-top-ten --json --output semgrep-results.json . || true
|
|
||||||
|
|
||||||
- name: Upload Semgrep to DefectDojo
|
|
||||||
env:
|
|
||||||
DD_URL: ${{ secrets.DD_URL }}
|
|
||||||
DD_TOKEN: ${{ secrets.DD_TOKEN }}
|
|
||||||
run: |
|
|
||||||
echo "Uploading Semgrep results..."
|
|
||||||
TODAY=$(date +%Y-%m-%d)
|
|
||||||
|
|
||||||
HTTP_CODE=$(curl --write-out "%{http_code}" --output response.txt --silent -X POST "$DD_URL/api/v2/import-scan/" \
|
|
||||||
-H "Authorization: Token $DD_TOKEN" \
|
|
||||||
-F "active=true" \
|
|
||||||
-F "verified=true" \
|
|
||||||
-F "scan_type=Semgrep JSON Report" \
|
|
||||||
-F "engagement_name=CI/CD Pipeline" \
|
|
||||||
-F "product_name=${{ github.repository }}" \
|
|
||||||
-F "scan_date=$TODAY" \
|
|
||||||
-F "auto_create_context=true" \
|
|
||||||
-F "file=@semgrep-results.json")
|
|
||||||
|
|
||||||
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
|
|
||||||
echo "::error::Upload Failed with HTTP $HTTP_CODE"
|
|
||||||
echo "--- SERVER RESPONSE ---"
|
|
||||||
cat response.txt
|
|
||||||
echo "-----------------------"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "Upload Success!"
|
|
||||||
fi
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Code Analysis
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sonar:
|
||||||
|
name: SonarQube
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Source Files
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: SonarCube Scan
|
||||||
|
uses: SonarSource/sonarqube-scan-action@v4
|
||||||
|
timeout-minutes: 10
|
||||||
|
env:
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
SONAR_HOST_URL: "https://quality.nhcarrigan.com"
|
||||||
|
with:
|
||||||
|
args: >
|
||||||
|
-Dsonar.sources=.
|
||||||
|
-Dsonar.projectKey=nutrition-tracker
|
||||||
|
|
||||||
|
- name: SonarQube Quality Gate check
|
||||||
|
uses: sonarsource/sonarqube-quality-gate-action@v1
|
||||||
|
with:
|
||||||
|
pollingTimeoutSec: 600
|
||||||
|
env:
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
SONAR_HOST_URL: "https://quality.nhcarrigan.com"
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Package Manager Configuration
|
|
||||||
# Force pnpm usage - breaks npm/yarn intentionally
|
|
||||||
node-linker=pnpm
|
|
||||||
|
|
||||||
# Security: Disable all lifecycle scripts
|
|
||||||
ignore-scripts=true
|
|
||||||
enable-pre-post-scripts=false
|
|
||||||
|
|
||||||
# Security: Require packages to be 10+ days old before installation
|
|
||||||
minimum-release-age=14400
|
|
||||||
|
|
||||||
# Security: Verify package integrity hashes
|
|
||||||
verify-store-integrity=true
|
|
||||||
|
|
||||||
# Security: Enforce strict trust policies
|
|
||||||
trust-policy=strict
|
|
||||||
|
|
||||||
# Security: Strict peer dependency resolution
|
|
||||||
strict-peer-dependencies=true
|
|
||||||
|
|
||||||
# Performance: Use symlinks for node_modules
|
|
||||||
symlink=true
|
|
||||||
|
|
||||||
# Lockfile: Ensure lockfile is not modified during install
|
|
||||||
frozen-lockfile=false
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
# Vitalia
|
# Nutrition Tracker
|
||||||
|
|
||||||
Vitalia aims to be a complete nutrition tracker with all the bells and whistles, but none of the paywalls. She's here to help you reach your fitness goals!
|
This application is a tool I can use to track my daily nutrition intake. The hosted instance requires a password to access, but you are welcome to use this to host your own tool!
|
||||||
|
|
||||||
## Live Version
|
## Live Version
|
||||||
|
|
||||||
This page is currently deployed. [View the live website.](https://vitalia.nhcarrigan.com)
|
This page is currently deployed. [View the live website.](https://food.nhcarrigan.com)
|
||||||
|
|
||||||
## Feedback and Bugs
|
## Feedback and Bugs
|
||||||
|
|
||||||
If you have feedback or a bug report, please [log a ticket on our forum](https://support.nhcarrigan.com).
|
If you have feedback or a bug report, please feel free to open a GitHub issue!
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -14,5 +14,5 @@ import { RouterOutlet } from "@angular/router";
|
|||||||
templateUrl: "./app.component.html",
|
templateUrl: "./app.component.html",
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
public title = "Vitalia";
|
public title = "client";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1 @@
|
|||||||
<h1>Vitalia</h1>
|
<p>landing works!</p>
|
||||||
|
|
||||||
<img src="https://cdn.nhcarrigan.com/new-avatars/vitalia-full.png" alt="Vitalia" width="300">
|
|
||||||
|
|
||||||
<p>Vitalia is your nutritional assistant and companion, here to help you reach your fitness goals!</p>
|
|
||||||
|
|
||||||
<p>Stay tuned, we're hard at work to bring her to life!</p>
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Vitalia</title>
|
<title>Client</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
|||||||
+1
-1
@@ -21,6 +21,6 @@
|
|||||||
"turbo": "2.5.3"
|
"turbo": "2.5.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "7.3.0"
|
"@prisma/client": "6.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+7
-14
@@ -9,8 +9,8 @@ importers:
|
|||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@prisma/client':
|
'@prisma/client':
|
||||||
specifier: 7.3.0
|
specifier: 6.7.0
|
||||||
version: 7.3.0(prisma@6.7.0(typescript@5.8.3))(typescript@5.8.3)
|
version: 6.7.0(prisma@6.7.0(typescript@5.8.3))(typescript@5.8.3)
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@nhcarrigan/eslint-config':
|
'@nhcarrigan/eslint-config':
|
||||||
specifier: 5.2.0
|
specifier: 5.2.0
|
||||||
@@ -1598,15 +1598,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==}
|
resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==}
|
||||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||||
|
|
||||||
'@prisma/client-runtime-utils@7.3.0':
|
'@prisma/client@6.7.0':
|
||||||
resolution: {integrity: sha512-dG/ceD9c+tnXATPk8G+USxxYM9E6UdMTnQeQ+1SZUDxTz7SgQcfxEqafqIQHcjdlcNK/pvmmLfSwAs3s2gYwUw==}
|
resolution: {integrity: sha512-+k61zZn1XHjbZul8q6TdQLpuI/cvyfil87zqK2zpreNIXyXtpUv3+H/oM69hcsFcZXaokHJIzPAt5Z8C8eK2QA==}
|
||||||
|
engines: {node: '>=18.18'}
|
||||||
'@prisma/client@7.3.0':
|
|
||||||
resolution: {integrity: sha512-FXBIxirqQfdC6b6HnNgxGmU7ydCPEPk7maHMOduJJfnTP+MuOGa15X4omjR/zpPUUpm8ef/mEFQjJudOGkXFcQ==}
|
|
||||||
engines: {node: ^20.19 || ^22.12 || >=24.0}
|
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
prisma: '*'
|
prisma: '*'
|
||||||
typescript: '>=5.4.0'
|
typescript: '>=5.1.0'
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
prisma:
|
prisma:
|
||||||
optional: true
|
optional: true
|
||||||
@@ -7370,11 +7367,7 @@ snapshots:
|
|||||||
|
|
||||||
'@pkgr/core@0.1.2': {}
|
'@pkgr/core@0.1.2': {}
|
||||||
|
|
||||||
'@prisma/client-runtime-utils@7.3.0': {}
|
'@prisma/client@6.7.0(prisma@6.7.0(typescript@5.8.3))(typescript@5.8.3)':
|
||||||
|
|
||||||
'@prisma/client@7.3.0(prisma@6.7.0(typescript@5.8.3))(typescript@5.8.3)':
|
|
||||||
dependencies:
|
|
||||||
'@prisma/client-runtime-utils': 7.3.0
|
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
prisma: 6.7.0(typescript@5.8.3)
|
prisma: 6.7.0(typescript@5.8.3)
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
|
|||||||
@@ -7,6 +7,6 @@
|
|||||||
import { Logger } from "@nhcarrigan/logger";
|
import { Logger } from "@nhcarrigan/logger";
|
||||||
|
|
||||||
export const logger = new Logger(
|
export const logger = new Logger(
|
||||||
"Vitalia",
|
"Nutrition API",
|
||||||
process.env.LOG_TOKEN ?? "",
|
process.env.LOG_TOKEN ?? "",
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user