diff --git a/.gitea/workflows/cd-develop.yml b/.gitea/workflows/cd-develop.yml index adedc3a..a6058f6 100644 --- a/.gitea/workflows/cd-develop.yml +++ b/.gitea/workflows/cd-develop.yml @@ -130,18 +130,6 @@ jobs: docker compose pull hiveiq-aria && \ REGISTRY_URL=${{ secrets.DEV_REGISTRY_URL }} ARIA_VERSION=dev \ docker compose up -d hiveiq-aria" - - - name: Notify Slack - if: always() - run: | - STATUS="${{ job.status }}" - [ "$STATUS" = "success" ] && COLOR="good" || COLOR="danger" - SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) - RUN_URL="https://hiveiq-gitea.directlx.dev/hiveiq-src/hiveops-aria/actions/runs/${{ github.run_id }}" - curl -s -X POST "${{ secrets.SLACK_WEBHOOK_URL }}" \ - -H "Content-Type: application/json" \ - -d "{\"attachments\":[{\"color\":\"$COLOR\",\"text\":\"*<$RUN_URL|hiveops-aria CD Develop — $STATUS>*\n${{ github.ref_name }} · $SHORT_SHA · ${{ github.actor }}\",\"mrkdwn_in\":[\"text\"],\"footer\":\"Gitea Actions\"}]}" - - name: Cleanup if: always() run: rm -f ~/.ssh/deploy_key diff --git a/.gitea/workflows/cd-main.yml b/.gitea/workflows/cd-main.yml index 3d0d108..17c57a8 100644 --- a/.gitea/workflows/cd-main.yml +++ b/.gitea/workflows/cd-main.yml @@ -4,10 +4,11 @@ on: push: branches: - main + workflow_dispatch: jobs: build-and-deploy: - runs-on: ubuntu-latest + runs-on: production env: GIT_SSL_NO_VERIFY: "true" MAVEN_OPTS: "-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true" @@ -16,20 +17,12 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Verify toolchain + run: java -version && mvn -version && trivy --version + - name: Clean runner disk run: docker system prune -f 2>/dev/null || true - - name: Install Java 21 and Maven - run: | - apt-get update -qq - apt-get install -y -qq wget apt-transport-https gnupg - wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor -o /usr/share/keyrings/adoptium.gpg - echo "deb [signed-by=/usr/share/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb $(. /etc/os-release && echo $VERSION_CODENAME) main" | tee /etc/apt/sources.list.d/adoptium.list - apt-get update -qq - apt-get install -y -qq temurin-21-jdk maven - java -version - mvn -version - - name: Configure Maven settings run: | mkdir -p ~/.m2 @@ -73,9 +66,6 @@ jobs: - name: Build JAR run: mvn clean package -DskipTests -B - - name: Install Trivy - run: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin - - name: Trivy vulnerability scan run: | trivy fs \ @@ -83,6 +73,7 @@ jobs: --severity HIGH,CRITICAL \ --ignore-unfixed \ --scanners vuln \ + --skip-db-update \ --format table \ target/ @@ -98,16 +89,12 @@ jobs: -DautoUpdate=false \ -DdataDirectory=/tmp/owasp-data \ -DsuppressionFile=deployment/owasp-suppressions.xml \ + -DassemblyAnalyzerEnabled=false \ -Dformats=HTML,JSON \ -B - name: Build and push Docker image run: | - if ! command -v docker &> /dev/null; then - apt-get update -qq && apt-get install -y -qq docker.io - fi - DOCKER_BUILDKIT=0 docker system prune -f 2>/dev/null || true - export DOCKER_BUILDKIT=0 echo "${{ secrets.REGISTRY_PASSWORD }}" | \ docker login ${{ secrets.REGISTRY_URL }} \ -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin @@ -117,14 +104,3 @@ jobs: -t ${{ secrets.REGISTRY_URL }}/hiveiq-aria:${SHORT_SHA} . docker push ${{ secrets.REGISTRY_URL }}/hiveiq-aria:latest docker push ${{ secrets.REGISTRY_URL }}/hiveiq-aria:${SHORT_SHA} - - - name: Notify Slack - if: always() - run: | - STATUS="${{ job.status }}" - [ "$STATUS" = "success" ] && COLOR="good" || COLOR="danger" - SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) - RUN_URL="https://hiveiq-gitea.directlx.dev/hiveiq-src/hiveops-aria/actions/runs/${{ github.run_id }}" - curl -s -X POST "${{ secrets.SLACK_PROD_WEBHOOK_URL }}" \ - -H "Content-Type: application/json" \ - -d "{\"attachments\":[{\"color\":\"$COLOR\",\"text\":\"*<$RUN_URL|hiveops-aria CD Production — $STATUS>*\n${{ github.ref_name }} · $SHORT_SHA · ${{ github.actor }}\",\"mrkdwn_in\":[\"text\"],\"footer\":\"Gitea Actions\"}]}" diff --git a/build.sh b/build.sh index 10cd8c2..4f9e62b 100755 --- a/build.sh +++ b/build.sh @@ -12,7 +12,14 @@ while [[ "$#" -gt 0 ]]; do shift done -IMAGE="$REGISTRY/hiveiq-aria:latest" +# Use :dev tag for the dev registry, :latest for everything else +if [[ "$REGISTRY" == *"bcos.dev"* ]]; then + TAG="dev" +else + TAG="latest" +fi + +IMAGE="$REGISTRY/hiveiq-aria:$TAG" echo "Building JAR..." mvn clean package -DskipTests -B diff --git a/frontend/entrypoint.sh b/frontend/entrypoint.sh index e76c3bb..b368b7c 100644 --- a/frontend/entrypoint.sh +++ b/frontend/entrypoint.sh @@ -1,19 +1,12 @@ #!/bin/sh set -e -# Inject runtime config from environment variables into the built SPA. -# This file is written to /app/dist/config.js and loaded by index.html -# BEFORE the Vite bundle — so window.__APP_CONFIG__ is available at startup. -# -# Add new config values here and in src/lib/api.ts Window interface. - cat > /app/dist/config.js < { document.documentElement.classList.add('dark-mode'); - authApi.get('/api/auth/users/me').then((res: any) => { + authApi.get('/auth/api/users/me').then((res: any) => { userInfo = { name: res.data.name, email: res.data.email, role: res.data.role }; }).catch(() => {}); }); diff --git a/frontend/src/components/DevicePosture.svelte b/frontend/src/components/DevicePosture.svelte index 1a79bc7..3873071 100644 --- a/frontend/src/components/DevicePosture.svelte +++ b/frontend/src/components/DevicePosture.svelte @@ -122,16 +122,17 @@ // Auto-refresh const REFRESH_SECONDS = 60; - const CIRCUMFERENCE = 2 * Math.PI * 14; let secondsLeft = REFRESH_SECONDS; let refreshing = false; - let autoRefreshEnabled = true; + const _AR_KEY = 'aria_posture_autoRefresh'; + let autoRefreshEnabled: boolean = typeof localStorage !== 'undefined' + ? (localStorage.getItem(_AR_KEY) ?? 'true') === 'true' + : true; let tickInterval: ReturnType; - $: dashOffset = CIRCUMFERENCE * (1 - secondsLeft / REFRESH_SECONDS); - function toggleAutoRefresh() { autoRefreshEnabled = !autoRefreshEnabled; + if (typeof localStorage !== 'undefined') localStorage.setItem(_AR_KEY, String(autoRefreshEnabled)); if (autoRefreshEnabled) secondsLeft = REFRESH_SECONDS; } @@ -166,32 +167,13 @@

Per-device security compliance — disk encryption, image integrity, OS currency

+ -
@@ -570,6 +552,39 @@ .toolbar { display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; } .toolbar-count { font-size: var(--font-size-body-sm); color: #6b7280; } .header-controls { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; } + .auto-refresh-toggle { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; + user-select: none; + flex-shrink: 0; + } + .auto-refresh-toggle input { display: none; } + .toggle-track { + width: 36px; height: 20px; + background: rgba(255,255,255,0.25); + border-radius: 10px; + position: relative; + transition: background 0.2s; + flex-shrink: 0; + } + .auto-refresh-toggle input:checked + .toggle-track { background: #34d399; } + .toggle-thumb { + position: absolute; + top: 3px; left: 3px; + width: 14px; height: 14px; + background: white; + border-radius: 50%; + transition: transform 0.2s; + } + .auto-refresh-toggle input:checked + .toggle-track .toggle-thumb { transform: translateX(16px); } + .toggle-label { + color: rgba(255,255,255,0.9); + font-size: 0.8rem; + white-space: nowrap; + min-width: 110px; + } .btn-manual-refresh { background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.3); color: white; border-radius: 50%; width: 36px; height: 36px; @@ -578,16 +593,6 @@ } .btn-manual-refresh:hover:not(:disabled) { background: rgba(255,255,255,0.28); } .btn-manual-refresh:disabled { opacity: 0.5; cursor: not-allowed; } - .refresh-countdown { - flex-shrink: 0; background: none; border: none; cursor: pointer; padding: 0; - border-radius: 50%; display: flex; align-items: center; justify-content: center; - opacity: 0.9; transition: opacity 0.15s, transform 0.15s; - } - .refresh-countdown:hover { opacity: 1; transform: scale(1.08); } - .refresh-countdown.paused { opacity: 0.5; } - .refresh-countdown.paused:hover { opacity: 0.8; } - @keyframes countdown-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } - :global(.pulsing) { animation: countdown-pulse 0.8s ease-in-out infinite; } /* Table card — canonical */ .table-card { diff --git a/frontend/src/components/PrecursorAlerts.svelte b/frontend/src/components/PrecursorAlerts.svelte index f5d99a3..7643799 100644 --- a/frontend/src/components/PrecursorAlerts.svelte +++ b/frontend/src/components/PrecursorAlerts.svelte @@ -110,16 +110,17 @@ // Auto-refresh const REFRESH_SECONDS = 60; - const CIRCUMFERENCE = 2 * Math.PI * 14; let secondsLeft = REFRESH_SECONDS; let refreshing = false; - let autoRefreshEnabled = true; + const _AR_KEY = 'aria_alerts_autoRefresh'; + let autoRefreshEnabled: boolean = typeof localStorage !== 'undefined' + ? (localStorage.getItem(_AR_KEY) ?? 'true') === 'true' + : true; let tickInterval: ReturnType; - $: dashOffset = CIRCUMFERENCE * (1 - secondsLeft / REFRESH_SECONDS); - function toggleAutoRefresh() { autoRefreshEnabled = !autoRefreshEnabled; + if (typeof localStorage !== 'undefined') localStorage.setItem(_AR_KEY, String(autoRefreshEnabled)); if (autoRefreshEnabled) secondsLeft = REFRESH_SECONDS; } @@ -162,32 +163,13 @@

Multi-phase attack sequence detection — jackpotting and skimming precursors

+ -
@@ -551,6 +533,39 @@ .toolbar-count { font-size: var(--font-size-body-sm); color: #6b7280; } .header-controls { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; } + .auto-refresh-toggle { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; + user-select: none; + flex-shrink: 0; + } + .auto-refresh-toggle input { display: none; } + .toggle-track { + width: 36px; height: 20px; + background: rgba(255,255,255,0.25); + border-radius: 10px; + position: relative; + transition: background 0.2s; + flex-shrink: 0; + } + .auto-refresh-toggle input:checked + .toggle-track { background: #34d399; } + .toggle-thumb { + position: absolute; + top: 3px; left: 3px; + width: 14px; height: 14px; + background: white; + border-radius: 50%; + transition: transform 0.2s; + } + .auto-refresh-toggle input:checked + .toggle-track .toggle-thumb { transform: translateX(16px); } + .toggle-label { + color: rgba(255,255,255,0.9); + font-size: 0.8rem; + white-space: nowrap; + min-width: 110px; + } .btn-manual-refresh { background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.3); color: white; border-radius: 50%; width: 36px; height: 36px; @@ -559,16 +574,6 @@ } .btn-manual-refresh:hover:not(:disabled) { background: rgba(255,255,255,0.28); } .btn-manual-refresh:disabled { opacity: 0.5; cursor: not-allowed; } - .refresh-countdown { - flex-shrink: 0; background: none; border: none; cursor: pointer; padding: 0; - border-radius: 50%; display: flex; align-items: center; justify-content: center; - opacity: 0.9; transition: opacity 0.15s, transform 0.15s; - } - .refresh-countdown:hover { opacity: 1; transform: scale(1.08); } - .refresh-countdown.paused { opacity: 0.5; } - .refresh-countdown.paused:hover { opacity: 0.8; } - @keyframes countdown-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } - :global(.pulsing) { animation: countdown-pulse 0.8s ease-in-out infinite; } /* Table card — canonical */ .table-card { diff --git a/frontend/src/components/ThreatEvents.svelte b/frontend/src/components/ThreatEvents.svelte index 7bc0cfb..d558598 100644 --- a/frontend/src/components/ThreatEvents.svelte +++ b/frontend/src/components/ThreatEvents.svelte @@ -85,16 +85,17 @@ // Auto-refresh const REFRESH_SECONDS = 60; - const CIRCUMFERENCE = 2 * Math.PI * 14; let secondsLeft = REFRESH_SECONDS; let refreshing = false; - let autoRefreshEnabled = true; + const _AR_KEY = 'aria_threats_autoRefresh'; + let autoRefreshEnabled: boolean = typeof localStorage !== 'undefined' + ? (localStorage.getItem(_AR_KEY) ?? 'true') === 'true' + : true; let tickInterval: ReturnType; - $: dashOffset = CIRCUMFERENCE * (1 - secondsLeft / REFRESH_SECONDS); - function toggleAutoRefresh() { autoRefreshEnabled = !autoRefreshEnabled; + if (typeof localStorage !== 'undefined') localStorage.setItem(_AR_KEY, String(autoRefreshEnabled)); if (autoRefreshEnabled) secondsLeft = REFRESH_SECONDS; } @@ -128,32 +129,13 @@

IOC match events detected across the fleet

+ -
@@ -290,6 +272,39 @@ } .header-controls { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; } + .auto-refresh-toggle { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; + user-select: none; + flex-shrink: 0; + } + .auto-refresh-toggle input { display: none; } + .toggle-track { + width: 36px; height: 20px; + background: rgba(255,255,255,0.25); + border-radius: 10px; + position: relative; + transition: background 0.2s; + flex-shrink: 0; + } + .auto-refresh-toggle input:checked + .toggle-track { background: #34d399; } + .toggle-thumb { + position: absolute; + top: 3px; left: 3px; + width: 14px; height: 14px; + background: white; + border-radius: 50%; + transition: transform 0.2s; + } + .auto-refresh-toggle input:checked + .toggle-track .toggle-thumb { transform: translateX(16px); } + .toggle-label { + color: rgba(255,255,255,0.9); + font-size: 0.8rem; + white-space: nowrap; + min-width: 110px; + } .btn-manual-refresh { background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.3); color: white; border-radius: 50%; width: 36px; height: 36px; @@ -298,16 +313,6 @@ } .btn-manual-refresh:hover:not(:disabled) { background: rgba(255,255,255,0.28); } .btn-manual-refresh:disabled { opacity: 0.5; cursor: not-allowed; } - .refresh-countdown { - flex-shrink: 0; background: none; border: none; cursor: pointer; padding: 0; - border-radius: 50%; display: flex; align-items: center; justify-content: center; - opacity: 0.9; transition: opacity 0.15s, transform 0.15s; - } - .refresh-countdown:hover { opacity: 1; transform: scale(1.08); } - .refresh-countdown.paused { opacity: 0.5; } - .refresh-countdown.paused:hover { opacity: 0.8; } - @keyframes countdown-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } - :global(.pulsing) { animation: countdown-pulse 0.8s ease-in-out infinite; } .page-body { display: flex; flex: 1; overflow: hidden; min-height: 0; margin: 0 0 16px; diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index c68e460..6849e36 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -4,21 +4,20 @@ declare global { interface Window { __APP_CONFIG__?: { apiUrl?: string; - authUrl?: string; }; } } -const API_BASE = window.__APP_CONFIG__?.apiUrl || 'http://localhost:8017'; -const AUTH_BASE = window.__APP_CONFIG__?.authUrl || 'http://localhost:8082'; +const ariaApiUrl = window.__APP_CONFIG__?.apiUrl || 'http://localhost:8017'; +const gatewayUrl = ariaApiUrl.replace(/\/aria$/, ''); const api = axios.create({ - baseURL: API_BASE, + baseURL: ariaApiUrl, headers: { 'Content-Type': 'application/json' }, }); export const authApi = axios.create({ - baseURL: AUTH_BASE, + baseURL: gatewayUrl, headers: { 'Content-Type': 'application/json' }, }); diff --git a/src/main/java/com/hiveops/aria/config/SecurityConfig.java b/src/main/java/com/hiveops/aria/config/SecurityConfig.java index 799e953..1229225 100644 --- a/src/main/java/com/hiveops/aria/config/SecurityConfig.java +++ b/src/main/java/com/hiveops/aria/config/SecurityConfig.java @@ -10,12 +10,6 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - -import java.util.Arrays; -import java.util.List; @Configuration @EnableWebSecurity @@ -31,7 +25,7 @@ public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http - .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .cors(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable) .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth @@ -46,29 +40,4 @@ public class SecurityConfig { return http.build(); } - - @Bean - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration config = new CorsConfiguration(); - - String originsEnv = System.getenv("CORS_ALLOWED_ORIGINS"); - if (originsEnv != null && !originsEnv.isBlank()) { - config.setAllowedOrigins(Arrays.asList(originsEnv.split(","))); - } else { - config.setAllowedOrigins(List.of( - "https://aria.bcos.cloud", - "https://portal.bcos.cloud", - "http://localhost:5181" - )); - } - - config.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")); - config.setAllowedHeaders(List.of("Authorization", "Content-Type", "X-Requested-With", "X-Service-Secret")); - config.setAllowCredentials(true); - config.setMaxAge(3600L); - - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", config); - return source; - } }