diff --git a/.github/scripts/prepare-pages-site.sh b/.github/scripts/prepare-pages-site.sh index 921ff04..d09ab97 100644 --- a/.github/scripts/prepare-pages-site.sh +++ b/.github/scripts/prepare-pages-site.sh @@ -1,7 +1,9 @@ #!/usr/bin/env bash set -euo pipefail +export LANG=C.UTF-8 LC_ALL=C.UTF-8 # Builds _site/ for GitHub Project Pages (https://.github.io/Oswl/). +# Landing HTML lives in landing/ (not served by the Spring Boot app). # Usage: .github/scripts/prepare-pages-site.sh [owner/repo] REPO="${1:-}" @@ -9,7 +11,9 @@ ROOT="$(cd "$(dirname "$0")/../.." && pwd)" cd "$ROOT" mkdir -p _site -cp src/main/resources/static/landing/index.html _site/index.html +cp landing/index.html _site/index.html +cp landing/landing-i18n.js _site/ +cp -r landing/i18n _site/ cp -r src/main/resources/static/css _site/ cp -r src/main/resources/static/icon _site/ cp -r src/main/resources/static/graphic _site/ @@ -27,10 +31,14 @@ sed -i \ -e 's|src="/icon/|src="icon/|g' \ -e 's|src="/graphic/|src="graphic/|g' \ -e 's|src="/img/|src="img/|g' \ + -e 's|src="/landing-i18n.js"|src="landing-i18n.js"|g' \ -e "s|href=\"/oss-notices\"|href=\"${LICENSE_LINK}\"|g" \ _site/index.html test -f _site/index.html +test -f _site/landing-i18n.js +test -f _site/i18n/en.json +test -f _site/i18n/ko.json test -f _site/css/tailwind.css test -f _site/icon/icon-logo.svg test -f _site/graphic/symbol_w.svg diff --git a/_site/i18n/en.json b/_site/i18n/en.json new file mode 100644 index 0000000..b24d2e9 --- /dev/null +++ b/_site/i18n/en.json @@ -0,0 +1,130 @@ +{ + "meta": { + "title": "OsWL - Enterprise SCA Platform", + "description": "OsWL is an enterprise-grade Software Composition Analysis platform that continuously tracks CVE vulnerabilities and license risks across your open source dependencies." + }, + "nav": { + "features": "Features", + "platform": "Platform", + "howItWorks": "How It Works", + "themeToggle": "Toggle light/dark theme" + }, + "hero": { + "badge": "Enterprise SCA Platform", + "title": "Know Every Vulnerability
in Your Open Source Stack", + "subtitle": "OsWL continuously scans your dependencies for CVEs, license obligations, and transitive risks — giving security and compliance teams complete SBOM visibility from a single self-hosted platform.", + "ctaPrimary": "Explore Capabilities", + "ctaSecondary": "See the Platform" + }, + "stats": { + "multiSource": "Multi-source", + "multiSourceLabel": "GHSA · OSV Advisories", + "ecosystemsLabel": "Package Ecosystems", + "onDemand": "On-demand", + "onDemandLabel": "deps.dev Enrichment", + "cvssLabel": "Scoring Supported", + "gitNative": "Git-native", + "gitNativeLabel": "GitHub · GitLab · Bitbucket" + }, + "features": { + "label": "Core Capabilities", + "title": "End-to-End Software
Composition Analysis", + "subtitle": "From dependency ingestion to remediation, OsWL covers the full SCA lifecycle with enterprise-grade accuracy and auditability.", + "cveTitle": "CVE Vulnerability Tracking", + "cveDesc": "On-demand enrichment from deps.dev and OSV. Every component is cross-referenced against CVSS 3.x scores and patch availability.", + "cveTag": "deps.dev · OSV · GHSA", + "licenseTitle": "License Risk Management", + "licenseDesc": "Automatic SPDX classification of OSS licenses — copyleft, permissive, proprietary — with obligation rules, conflict detection, and NOTICE / SPDX SBOM export per scan.", + "licenseTag": "GPL · LGPL · Apache · MIT · AGPL · MPL", + "riskTitle": "Risk Trend Analysis", + "riskDesc": "Track your security posture across scans. Visualize critical, high, medium, and low severity trends over time, with optional AI-generated deviation insights when an LLM provider is configured.", + "riskTag": "Version-over-version delta", + "transitiveTitle": "Transitive Dependency Traversal", + "transitiveDesc": "Uncover vulnerabilities hidden deep in your dependency graph. DependencyPath analysis traces both direct and transitive exposure across every supported manifest.", + "transitiveTag": "Maven · npm · PyPI · Go · Cargo · NuGet · RubyGems", + "gitTitle": "Git-Native Repository Integration", + "gitDesc": "Connect GitHub, GitLab, or Bitbucket via Personal Access Token. Branch-aware imports, per-version snapshots, and per-project scan history out of the box.", + "gitTag": "GitHub · GitLab · Bitbucket", + "cliTitle": "CLI & CI/CD Pipeline Scanning", + "cliDesc": "Integrate OsWL into any build pipeline with a single CLI command. Only parsed manifest metadata is uploaded — no source code leaves your build host. Bearer-token API keys per project or organization-wide.", + "cliTag": "Manifest-only upload" + }, + "platform": { + "label": "Live Platform", + "title": "Built for Security &
Compliance Teams", + "subtitle": "Every screen is engineered for actionability — from CVE triage to remediation tracking, no context switching required.", + "tabSc": "Security Center", + "tabProjects": "Projects", + "tabLicense": "License Analysis", + "tabRisk": "Risk Trend", + "altSc": "OsWL Security Center — CVE triage with CVSS scores", + "altProjects": "OsWL Projects Dashboard — multi-project risk overview", + "altLicense": "OsWL License Analysis — copyleft and compliance risk tracking", + "altRisk": "OsWL Risk Trend — scan-over-scan security posture" + }, + "vuln": { + "label": "Vulnerability Intelligence", + "title": "Prioritize What
Actually Matters", + "desc": "OsWL surfaces exploitable, patchable vulnerabilities first. CVSS scores, exploit maturity, and fix availability are enriched automatically from deps.dev and OSV advisory feeds.", + "bullet1": "Critical & High vulnerabilities flagged with patch targets", + "bullet2": "Fix version surfaced — know if a patched release exists before triaging", + "bullet3": "Transitive exposure traced to root dependency paths", + "bullet4": "Bulk status updates — mark findings reviewed or ignored in one action", + "demoTitle": "backend-api · v3.2.0 — Security Center" + }, + "table": { + "component": "Component", + "cveId": "CVE ID", + "cvss": "CVSS", + "severity": "Severity" + }, + "severity": { + "critical": "Critical", + "high": "High", + "medium": "Medium" + }, + "licCard": { + "gpl2Type": "Copyleft · Strong", + "lgplType": "Copyleft · Weak", + "apacheType": "Permissive", + "mitType": "Permissive", + "mplType": "Copyleft · File-level", + "proprietaryType": "Commercial", + "restricted": "Restricted", + "caution": "Caution", + "permitted": "Permitted", + "unknown": "Unknown" + }, + "license": { + "label": "License Intelligence", + "title": "Eliminate
Compliance Blind Spots", + "desc": "Automatically classify every SPDX license across your dependency tree. Identify copyleft obligations, license conflicts, and components that need manual legal review before they reach production.", + "bullet1": "Copyleft licenses requiring full source disclosure", + "bullet2": "Weak copyleft or dual-licensed — legal review recommended", + "bullet3": "Permissive licenses — safe for commercial use" + }, + "workflow": { + "label": "Workflow", + "title": "Up and Scanning
in Minutes", + "subtitle": "OsWL integrates directly into your existing development workflow. No agents, no heavyweight setup.", + "step1Title": "Connect Your Repository", + "step1Desc": "Add a GitHub, GitLab, or Bitbucket Personal Access Token. OsWL discovers repositories and branches and imports manifests — no source code upload required.", + "step2Title": "Run a Dependency Scan", + "step2Desc": "OsWL parses your lock files and build manifests, resolves the transitive dependency graph, and cross-references every component against deps.dev, OSV, and your license policy.", + "step3Title": "Triage & Remediate", + "step3Desc": "Review findings in the Security Center, filter by severity, bulk-update statuses, and track your risk posture scan-over-scan in the Risk Trend dashboard.", + "step4Title": "Automate in CI/CD", + "step4Desc": "Gate pull requests with the OsWL CLI. Bearer-token API keys enable automated scanning in any pipeline — Jenkins, GitHub Actions, GitLab CI, or custom build servers." + }, + "cta": { + "title": "Secure Your
Open Source Stack", + "sub": "Deploy OsWL on-premise and gain full SBOM visibility across every project, team, and dependency in your organization.", + "primary": "See How It Works →", + "secondary": "Explore Features" + }, + "footer": { + "copyright": "Copyright © 2026 OsWL, Licensed under the MIT License.", + "ossNotices": "Open Source Notices", + "langLabel": "Language" + } +} diff --git a/_site/i18n/ko.json b/_site/i18n/ko.json new file mode 100644 index 0000000..ae19faa --- /dev/null +++ b/_site/i18n/ko.json @@ -0,0 +1,130 @@ +{ + "meta": { + "title": "OsWL - 엔터프라이즈 SCA 플랫폼", + "description": "OsWL은 오픈소스 의존성의 CVE 취약점과 라이선스 리스크를 지속적으로 추적하는 엔터프라이즈급 소프트웨어 구성 분석(SCA) 플랫폼입니다." + }, + "nav": { + "features": "기능", + "platform": "플랫폼", + "howItWorks": "사용 방법", + "themeToggle": "라이트/다크 테마 전환" + }, + "hero": { + "badge": "엔터프라이즈 SCA 플랫폼", + "title": "오픈소스 스택
모든 취약점을 파악하세요", + "subtitle": "OsWL은 의존성의 CVE, 라이선스 의무, 전이적 리스크를 지속적으로 스캔하여 보안·컴플라이언스 팀이 단일 자체 호스팅 플랫폼에서 완전한 SBOM 가시성을 확보할 수 있게 합니다.", + "ctaPrimary": "기능 살펴보기", + "ctaSecondary": "플랫폼 보기" + }, + "stats": { + "multiSource": "다중 소스", + "multiSourceLabel": "GHSA · OSV Advisories", + "ecosystemsLabel": "패키지 생태계", + "onDemand": "온디맨드", + "onDemandLabel": "deps.dev 보강", + "cvssLabel": "점수 산정 지원", + "gitNative": "Git 네이티브", + "gitNativeLabel": "GitHub · GitLab · Bitbucket" + }, + "features": { + "label": "핵심 기능", + "title": "엔드투엔드
소프트웨어 구성 분석", + "subtitle": "의존성 수집부터 조치까지, OsWL은 엔터프라이즈급 정확도와 감사 추적 가능성으로 전체 SCA 라이프사이클을 커버합니다.", + "cveTitle": "CVE 취약점 추적", + "cveDesc": "deps.dev와 OSV에서 온디맨드로 보강합니다. 모든 컴포넌트를 CVSS 3.x 점수 및 패치 가능 여부와 교차 검증합니다.", + "cveTag": "deps.dev · OSV · GHSA", + "licenseTitle": "라이선스 리스크 관리", + "licenseDesc": "OSS 라이선스의 SPDX 자동 분류 — 카피레프트, 허용적, 독점 — 의무 규칙, 충돌 감지, 스캔별 NOTICE / SPDX SBOM 출력을 제공합니다.", + "licenseTag": "GPL · LGPL · Apache · MIT · AGPL · MPL", + "riskTitle": "리스크 트렌드 분석", + "riskDesc": "스캔 간 보안 상태를 추적합니다. Critical, High, Medium, Low 심각도 추이를 시각화하고, LLM 프로바이더 설정 시 AI 생성 편차 인사이트를 선택적으로 제공합니다.", + "riskTag": "버전 간 델타", + "transitiveTitle": "전이적 의존성 탐색", + "transitiveDesc": "의존성 그래프 깊숙이 숨은 취약점을 발견합니다. DependencyPath 분석이 지원되는 모든 매니페스트에서 직접·전이적 노출을 추적합니다.", + "transitiveTag": "Maven · npm · PyPI · Go · Cargo · NuGet · RubyGems", + "gitTitle": "Git 네이티브 저장소 연동", + "gitDesc": "Personal Access Token으로 GitHub, GitLab, Bitbucket을 연결합니다. 브랜치 인식 임포트, 버전별 스냅샷, 프로젝트별 스캔 히스토리를 기본 제공합니다.", + "gitTag": "GitHub · GitLab · Bitbucket", + "cliTitle": "CLI & CI/CD 파이프라인 스캔", + "cliDesc": "단일 CLI 명령으로 모든 빌드 파이프라인에 OsWL을 통합합니다. 파싱된 매니페스트 메타데이터만 업로드되며 소스 코드는 빌드 호스트를 벗어나지 않습니다. 프로젝트별 또는 조직 전체 Bearer 토큰 API 키를 지원합니다.", + "cliTag": "매니페스트 전용 업로드" + }, + "platform": { + "label": "라이브 플랫폼", + "title": "보안 &
컴플라이언스 팀을 위해", + "subtitle": "모든 화면은 실행 가능성을 위해 설계되었습니다 — CVE 트리아지부터 조치 추적까지, 컨텍스트 전환 없이 작업할 수 있습니다.", + "tabSc": "보안 센터", + "tabProjects": "프로젝트", + "tabLicense": "라이선스 분석", + "tabRisk": "리스크 트렌드", + "altSc": "OsWL 보안 센터 — CVSS 점수 기반 CVE 트리아지", + "altProjects": "OsWL 프로젝트 대시보드 — 다중 프로젝트 리스크 개요", + "altLicense": "OsWL 라이선스 분석 — 카피레프트 및 컴플라이언스 리스크 추적", + "altRisk": "OsWL 리스크 트렌드 — 스캔 간 보안 상태" + }, + "vuln": { + "label": "취약점 인텔리전스", + "title": "진짜 중요한 것부터
우선순위를 정하세요", + "desc": "OsWL은 악용 가능하고 패치 가능한 취약점을 먼저 표면화합니다. CVSS 점수, 익스플로잇 성숙도, 수정 가능 여부가 deps.dev와 OSV 권고 피드에서 자동으로 보강됩니다.", + "bullet1": "Critical & High 취약점을 패치 대상과 함께 표시", + "bullet2": "수정 버전 표시 — 트리아지 전에 패치된 릴리스 존재 여부 확인", + "bullet3": "전이적 노출을 루트 의존성 경로까지 추적", + "bullet4": "일괄 상태 업데이트 — 한 번에 검토 완료 또는 무시 처리", + "demoTitle": "backend-api · v3.2.0 — 보안 센터" + }, + "table": { + "component": "컴포넌트", + "cveId": "CVE ID", + "cvss": "CVSS", + "severity": "심각도" + }, + "severity": { + "critical": "Critical", + "high": "High", + "medium": "Medium" + }, + "licCard": { + "gpl2Type": "카피레프트 · 강력", + "lgplType": "카피레프트 · 약함", + "apacheType": "허용적", + "mitType": "허용적", + "mplType": "카피레프트 · 파일 단위", + "proprietaryType": "상용", + "restricted": "제한", + "caution": "주의", + "permitted": "허용", + "unknown": "미확인" + }, + "license": { + "label": "라이선스 인텔리전스", + "title": "컴플라이언스
사각지대를 제거하세요", + "desc": "의존성 트리의 모든 SPDX 라이선스를 자동 분류합니다. 카피레프트 의무, 라이선스 충돌, 프로덕션 배포 전 법무 검토가 필요한 컴포넌트를 식별합니다.", + "bullet1": "전체 소스 공개가 필요한 카피레프트 라이선스", + "bullet2": "약한 카피레프트 또는 이중 라이선스 — 법무 검토 권장", + "bullet3": "허용적 라이선스 — 상업적 사용에 안전" + }, + "workflow": { + "label": "워크플로", + "title": "몇 분 안에
스캔을 시작하세요", + "subtitle": "OsWL은 기존 개발 워크플로에 직접 통합됩니다. 에이전트나 무거운 설정이 필요 없습니다.", + "step1Title": "저장소 연결", + "step1Desc": "GitHub, GitLab, Bitbucket Personal Access Token을 등록합니다. OsWL이 저장소와 브랜치를 탐색하고 매니페스트를 임포트합니다 — 소스 코드 업로드는 필요 없습니다.", + "step2Title": "의존성 스캔 실행", + "step2Desc": "OsWL이 lock 파일과 빌드 매니페스트를 파싱하고 전이적 의존성 그래프를 해석한 뒤, 모든 컴포넌트를 deps.dev, OSV, 라이선스 정책과 교차 검증합니다.", + "step3Title": "트리아지 & 조치", + "step3Desc": "보안 센터에서 결과를 검토하고, 심각도별로 필터링하며, 상태를 일괄 업데이트하고, 리스크 트렌드 대시보드에서 스캔 간 보안 상태를 추적합니다.", + "step4Title": "CI/CD 자동화", + "step4Desc": "OsWL CLI로 Pull Request를 게이트합니다. Bearer 토큰 API 키로 Jenkins, GitHub Actions, GitLab CI 또는 커스텀 빌드 서버 등 모든 파이프라인에서 자동 스캔이 가능합니다." + }, + "cta": { + "title": "오픈소스 스택
안전하게 보호하세요", + "sub": "OsWL을 온프레미스에 배포하고 조직의 모든 프로젝트, 팀, 의존성에 대한 완전한 SBOM 가시성을 확보하세요.", + "primary": "사용 방법 보기 →", + "secondary": "기능 살펴보기" + }, + "footer": { + "copyright": "Copyright © 2026 OsWL, Licensed under the MIT License.", + "ossNotices": "오픈소스 고지", + "langLabel": "언어" + } +} diff --git a/src/main/resources/static/landing/index.html b/_site/index.html similarity index 71% rename from src/main/resources/static/landing/index.html rename to _site/index.html index 174cbdb..55698ff 100644 --- a/src/main/resources/static/landing/index.html +++ b/_site/index.html @@ -3,8 +3,20 @@ - OsWL — Enterprise SCA Platform - + OsWL - Enterprise SCA Platform + + + + @@ -399,6 +411,11 @@ .footer-links { display: flex; gap: 24px; } .footer-link { font-size: 13px; color: var(--text-muted); transition: color 0.15s; } .footer-link:hover { color: var(--text-primary); } + .lang-switch { display: inline-flex; align-items: center; gap: 6px; margin-left: 4px; } + .lang-btn { background: none; border: none; cursor: pointer; font: inherit; font-size: 13px; color: var(--text-muted); padding: 0; transition: color 0.15s; } + .lang-btn:hover { color: var(--text-primary); } + .lang-btn.active { font-weight: 700; color: var(--text-primary); } + .lang-sep { color: var(--text-muted); opacity: 0.5; user-select: none; } /* ── Responsive ── */ @media (max-width: 900px) { @@ -431,11 +448,11 @@ OsWL - - - - + + + +
- OsWL Security Center — CVE triage with CVSS scores + OsWL Security Center — CVE triage with CVSS scores
- OsWL Projects Dashboard — multi-project risk overview + OsWL Projects Dashboard — multi-project risk overview
- OsWL License Analysis — copyleft and compliance risk tracking + OsWL License Analysis — copyleft and compliance risk tracking
- OsWL Risk Trend — scan-over-scan security posture + OsWL Risk Trend — scan-over-scan security posture
@@ -602,27 +619,27 @@

Built for Security &
Compliance Teams

- -

Prioritize What
Actually Matters

-

+

+

Prioritize What
Actually Matters

+

OsWL surfaces exploitable, patchable vulnerabilities first. CVSS scores, exploit maturity, and fix availability are enriched automatically from deps.dev and OSV advisory feeds.

- Critical & High vulnerabilities flagged with patch targets + Critical & High vulnerabilities flagged with patch targets
- Fix version surfaced — know if a patched release exists before triaging + Fix version surfaced — know if a patched release exists before triaging
- Transitive exposure traced to root dependency paths + Transitive exposure traced to root dependency paths
- Bulk status updates — mark findings reviewed or ignored in one action + Bulk status updates — mark findings reviewed or ignored in one action
@@ -631,40 +648,40 @@

Prioritize Wha
- backend-api · v3.2.0 — Security Center + backend-api · v3.2.0 — Security Center

- + - + - + - + - + - +
ComponentCVE IDCVSSSeverity
ComponentCVE IDCVSSSeverity
jackson-databind
2.13.4.2
CVE-2022-42003 9.8CriticalCritical
spring-webmvc
5.3.27
CVE-2023-20861 8.6HighHigh
commons-io
2.11.0
CVE-2024-22011 7.5HighHigh
logback-classic
1.2.11
CVE-2023-6378 6.2MediumMedium
guava
31.1-jre
CVE-2023-2976 5.5MediumMedium
@@ -674,31 +691,31 @@

Prioritize Wha
-
GPL-2.0-only
Copyleft · Strong
Restricted
-
LGPL-2.1-only
Copyleft · Weak
Caution
-
Apache-2.0
Permissive
Permitted
-
MIT
Permissive
Permitted
-
MPL-2.0
Copyleft · File-level
Caution
-
Proprietary
Commercial
Unknown
+
GPL-2.0-only
Copyleft · Strong
Restricted
+
LGPL-2.1-only
Copyleft · Weak
Caution
+
Apache-2.0
Permissive
Permitted
+
MIT
Permissive
Permitted
+
MPL-2.0
Copyleft · File-level
Caution
+
Proprietary
Commercial
Unknown
- -

Eliminate
Compliance Blind Spots

-

+

+

Eliminate
Compliance Blind Spots

+

Automatically classify every SPDX license across your dependency tree. Identify copyleft obligations, license conflicts, and components that need manual legal review before they reach production.

- Restricted - Copyleft licenses requiring full source disclosure + Restricted + Copyleft licenses requiring full source disclosure
- Caution - Weak copyleft or dual-licensed — legal review recommended + Caution + Weak copyleft or dual-licensed — legal review recommended
- Permitted - Permissive licenses — safe for commercial use + Permitted + Permissive licenses — safe for commercial use
@@ -710,30 +727,30 @@

Eliminate
C
- -

Up and Scanning
in Minutes

-

OsWL integrates directly into your existing development workflow. No agents, no heavyweight setup.

+ +

Up and Scanning
in Minutes

+

OsWL integrates directly into your existing development workflow. No agents, no heavyweight setup.

01
-

Connect Your Repository

-

Add a GitHub, GitLab, or Bitbucket Personal Access Token. OsWL discovers repositories and branches and imports manifests — no source code upload required.

+

Connect Your Repository

+

Add a GitHub, GitLab, or Bitbucket Personal Access Token. OsWL discovers repositories and branches and imports manifests — no source code upload required.

02
-

Run a Dependency Scan

-

OsWL parses your lock files and build manifests, resolves the transitive dependency graph, and cross-references every component against deps.dev, OSV, and your license policy.

+

Run a Dependency Scan

+

OsWL parses your lock files and build manifests, resolves the transitive dependency graph, and cross-references every component against deps.dev, OSV, and your license policy.

03
-

Triage & Remediate

-

Review findings in the Security Center, filter by severity, bulk-update statuses, and track your risk posture scan-over-scan in the Risk Trend dashboard.

+

Triage & Remediate

+

Review findings in the Security Center, filter by severity, bulk-update statuses, and track your risk posture scan-over-scan in the Risk Trend dashboard.

04
-

Automate in CI/CD

-

Gate pull requests with the OsWL CLI. Bearer-token API keys enable automated scanning in any pipeline — Jenkins, GitHub Actions, GitLab CI, or custom build servers.

+

Automate in CI/CD

+

Gate pull requests with the OsWL CLI. Bearer-token API keys enable automated scanning in any pipeline — Jenkins, GitHub Actions, GitLab CI, or custom build servers.

@@ -742,11 +759,11 @@

Automate in CI/CD

-

Secure Your
Open Source Stack

-

Deploy OsWL on-premise and gain full SBOM visibility across every project, team, and dependency in your organization.

+

Secure Your
Open Source Stack

+

Deploy OsWL on-premise and gain full SBOM visibility across every project, team, and dependency in your organization.

@@ -759,14 +776,19 @@

Secure Your
Open Source Stac OsWL

diff --git a/_site/landing-i18n.js b/_site/landing-i18n.js new file mode 100644 index 0000000..4e4f601 --- /dev/null +++ b/_site/landing-i18n.js @@ -0,0 +1,93 @@ +(function () { + 'use strict'; + + const STORAGE_KEY = 'oswl-landing-locale'; + const SUPPORTED = ['en', 'ko']; + + function detectLocale() { + const param = new URLSearchParams(location.search).get('lang'); + if (SUPPORTED.includes(param)) return param; + const stored = localStorage.getItem(STORAGE_KEY); + if (SUPPORTED.includes(stored)) return stored; + if ((navigator.language || '').toLowerCase().startsWith('ko')) return 'ko'; + return 'en'; + } + + function get(obj, path) { + return path.split('.').reduce((o, k) => (o && o[k] != null ? o[k] : null), obj); + } + + let currentLocale = 'en'; + + function applyMessages(messages) { + document.querySelectorAll('[data-i18n]').forEach(el => { + const val = get(messages, el.getAttribute('data-i18n')); + if (val != null) el.textContent = val; + }); + + document.querySelectorAll('[data-i18n-html]').forEach(el => { + const val = get(messages, el.getAttribute('data-i18n-html')); + if (val != null) el.innerHTML = val; + }); + + document.querySelectorAll('[data-i18n-attr]').forEach(el => { + el.getAttribute('data-i18n-attr').split(';').forEach(pair => { + const [attr, key] = pair.trim().split(':').map(s => s.trim()); + const val = get(messages, key); + if (val != null && attr) el.setAttribute(attr, val); + }); + }); + + const title = get(messages, 'meta.title'); + if (title) document.title = title; + + document.querySelectorAll('[data-set-lang]').forEach(btn => { + const lang = btn.getAttribute('data-set-lang'); + btn.classList.toggle('active', lang === currentLocale); + btn.setAttribute('aria-current', lang === currentLocale ? 'true' : 'false'); + }); + } + + async function loadAndApply(locale) { + currentLocale = locale; + document.documentElement.lang = locale; + + try { + const res = await fetch(`i18n/${locale}.json`); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + applyMessages(await res.json()); + } catch (e) { + console.warn('[landing-i18n] Failed to load locale:', locale, e); + } finally { + document.documentElement.classList.remove('i18n-pending'); + } + } + + function setLocale(locale) { + if (!SUPPORTED.includes(locale)) return; + localStorage.setItem(STORAGE_KEY, locale); + loadAndApply(locale); + } + + async function init() { + const param = new URLSearchParams(location.search).get('lang'); + if (SUPPORTED.includes(param)) { + localStorage.setItem(STORAGE_KEY, param); + const u = new URL(location.href); + u.searchParams.delete('lang'); + history.replaceState(null, '', u.pathname + u.search + u.hash); + } + + await loadAndApply(detectLocale()); + + document.querySelectorAll('[data-set-lang]').forEach(btn => { + btn.addEventListener('click', () => setLocale(btn.getAttribute('data-set-lang'))); + }); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); diff --git a/landing/i18n/en.json b/landing/i18n/en.json new file mode 100644 index 0000000..b24d2e9 --- /dev/null +++ b/landing/i18n/en.json @@ -0,0 +1,130 @@ +{ + "meta": { + "title": "OsWL - Enterprise SCA Platform", + "description": "OsWL is an enterprise-grade Software Composition Analysis platform that continuously tracks CVE vulnerabilities and license risks across your open source dependencies." + }, + "nav": { + "features": "Features", + "platform": "Platform", + "howItWorks": "How It Works", + "themeToggle": "Toggle light/dark theme" + }, + "hero": { + "badge": "Enterprise SCA Platform", + "title": "Know Every Vulnerability
in Your Open Source Stack", + "subtitle": "OsWL continuously scans your dependencies for CVEs, license obligations, and transitive risks — giving security and compliance teams complete SBOM visibility from a single self-hosted platform.", + "ctaPrimary": "Explore Capabilities", + "ctaSecondary": "See the Platform" + }, + "stats": { + "multiSource": "Multi-source", + "multiSourceLabel": "GHSA · OSV Advisories", + "ecosystemsLabel": "Package Ecosystems", + "onDemand": "On-demand", + "onDemandLabel": "deps.dev Enrichment", + "cvssLabel": "Scoring Supported", + "gitNative": "Git-native", + "gitNativeLabel": "GitHub · GitLab · Bitbucket" + }, + "features": { + "label": "Core Capabilities", + "title": "End-to-End Software
Composition Analysis", + "subtitle": "From dependency ingestion to remediation, OsWL covers the full SCA lifecycle with enterprise-grade accuracy and auditability.", + "cveTitle": "CVE Vulnerability Tracking", + "cveDesc": "On-demand enrichment from deps.dev and OSV. Every component is cross-referenced against CVSS 3.x scores and patch availability.", + "cveTag": "deps.dev · OSV · GHSA", + "licenseTitle": "License Risk Management", + "licenseDesc": "Automatic SPDX classification of OSS licenses — copyleft, permissive, proprietary — with obligation rules, conflict detection, and NOTICE / SPDX SBOM export per scan.", + "licenseTag": "GPL · LGPL · Apache · MIT · AGPL · MPL", + "riskTitle": "Risk Trend Analysis", + "riskDesc": "Track your security posture across scans. Visualize critical, high, medium, and low severity trends over time, with optional AI-generated deviation insights when an LLM provider is configured.", + "riskTag": "Version-over-version delta", + "transitiveTitle": "Transitive Dependency Traversal", + "transitiveDesc": "Uncover vulnerabilities hidden deep in your dependency graph. DependencyPath analysis traces both direct and transitive exposure across every supported manifest.", + "transitiveTag": "Maven · npm · PyPI · Go · Cargo · NuGet · RubyGems", + "gitTitle": "Git-Native Repository Integration", + "gitDesc": "Connect GitHub, GitLab, or Bitbucket via Personal Access Token. Branch-aware imports, per-version snapshots, and per-project scan history out of the box.", + "gitTag": "GitHub · GitLab · Bitbucket", + "cliTitle": "CLI & CI/CD Pipeline Scanning", + "cliDesc": "Integrate OsWL into any build pipeline with a single CLI command. Only parsed manifest metadata is uploaded — no source code leaves your build host. Bearer-token API keys per project or organization-wide.", + "cliTag": "Manifest-only upload" + }, + "platform": { + "label": "Live Platform", + "title": "Built for Security &
Compliance Teams", + "subtitle": "Every screen is engineered for actionability — from CVE triage to remediation tracking, no context switching required.", + "tabSc": "Security Center", + "tabProjects": "Projects", + "tabLicense": "License Analysis", + "tabRisk": "Risk Trend", + "altSc": "OsWL Security Center — CVE triage with CVSS scores", + "altProjects": "OsWL Projects Dashboard — multi-project risk overview", + "altLicense": "OsWL License Analysis — copyleft and compliance risk tracking", + "altRisk": "OsWL Risk Trend — scan-over-scan security posture" + }, + "vuln": { + "label": "Vulnerability Intelligence", + "title": "Prioritize What
Actually Matters", + "desc": "OsWL surfaces exploitable, patchable vulnerabilities first. CVSS scores, exploit maturity, and fix availability are enriched automatically from deps.dev and OSV advisory feeds.", + "bullet1": "Critical & High vulnerabilities flagged with patch targets", + "bullet2": "Fix version surfaced — know if a patched release exists before triaging", + "bullet3": "Transitive exposure traced to root dependency paths", + "bullet4": "Bulk status updates — mark findings reviewed or ignored in one action", + "demoTitle": "backend-api · v3.2.0 — Security Center" + }, + "table": { + "component": "Component", + "cveId": "CVE ID", + "cvss": "CVSS", + "severity": "Severity" + }, + "severity": { + "critical": "Critical", + "high": "High", + "medium": "Medium" + }, + "licCard": { + "gpl2Type": "Copyleft · Strong", + "lgplType": "Copyleft · Weak", + "apacheType": "Permissive", + "mitType": "Permissive", + "mplType": "Copyleft · File-level", + "proprietaryType": "Commercial", + "restricted": "Restricted", + "caution": "Caution", + "permitted": "Permitted", + "unknown": "Unknown" + }, + "license": { + "label": "License Intelligence", + "title": "Eliminate
Compliance Blind Spots", + "desc": "Automatically classify every SPDX license across your dependency tree. Identify copyleft obligations, license conflicts, and components that need manual legal review before they reach production.", + "bullet1": "Copyleft licenses requiring full source disclosure", + "bullet2": "Weak copyleft or dual-licensed — legal review recommended", + "bullet3": "Permissive licenses — safe for commercial use" + }, + "workflow": { + "label": "Workflow", + "title": "Up and Scanning
in Minutes", + "subtitle": "OsWL integrates directly into your existing development workflow. No agents, no heavyweight setup.", + "step1Title": "Connect Your Repository", + "step1Desc": "Add a GitHub, GitLab, or Bitbucket Personal Access Token. OsWL discovers repositories and branches and imports manifests — no source code upload required.", + "step2Title": "Run a Dependency Scan", + "step2Desc": "OsWL parses your lock files and build manifests, resolves the transitive dependency graph, and cross-references every component against deps.dev, OSV, and your license policy.", + "step3Title": "Triage & Remediate", + "step3Desc": "Review findings in the Security Center, filter by severity, bulk-update statuses, and track your risk posture scan-over-scan in the Risk Trend dashboard.", + "step4Title": "Automate in CI/CD", + "step4Desc": "Gate pull requests with the OsWL CLI. Bearer-token API keys enable automated scanning in any pipeline — Jenkins, GitHub Actions, GitLab CI, or custom build servers." + }, + "cta": { + "title": "Secure Your
Open Source Stack", + "sub": "Deploy OsWL on-premise and gain full SBOM visibility across every project, team, and dependency in your organization.", + "primary": "See How It Works →", + "secondary": "Explore Features" + }, + "footer": { + "copyright": "Copyright © 2026 OsWL, Licensed under the MIT License.", + "ossNotices": "Open Source Notices", + "langLabel": "Language" + } +} diff --git a/landing/i18n/ko.json b/landing/i18n/ko.json new file mode 100644 index 0000000..ae19faa --- /dev/null +++ b/landing/i18n/ko.json @@ -0,0 +1,130 @@ +{ + "meta": { + "title": "OsWL - 엔터프라이즈 SCA 플랫폼", + "description": "OsWL은 오픈소스 의존성의 CVE 취약점과 라이선스 리스크를 지속적으로 추적하는 엔터프라이즈급 소프트웨어 구성 분석(SCA) 플랫폼입니다." + }, + "nav": { + "features": "기능", + "platform": "플랫폼", + "howItWorks": "사용 방법", + "themeToggle": "라이트/다크 테마 전환" + }, + "hero": { + "badge": "엔터프라이즈 SCA 플랫폼", + "title": "오픈소스 스택
모든 취약점을 파악하세요", + "subtitle": "OsWL은 의존성의 CVE, 라이선스 의무, 전이적 리스크를 지속적으로 스캔하여 보안·컴플라이언스 팀이 단일 자체 호스팅 플랫폼에서 완전한 SBOM 가시성을 확보할 수 있게 합니다.", + "ctaPrimary": "기능 살펴보기", + "ctaSecondary": "플랫폼 보기" + }, + "stats": { + "multiSource": "다중 소스", + "multiSourceLabel": "GHSA · OSV Advisories", + "ecosystemsLabel": "패키지 생태계", + "onDemand": "온디맨드", + "onDemandLabel": "deps.dev 보강", + "cvssLabel": "점수 산정 지원", + "gitNative": "Git 네이티브", + "gitNativeLabel": "GitHub · GitLab · Bitbucket" + }, + "features": { + "label": "핵심 기능", + "title": "엔드투엔드
소프트웨어 구성 분석", + "subtitle": "의존성 수집부터 조치까지, OsWL은 엔터프라이즈급 정확도와 감사 추적 가능성으로 전체 SCA 라이프사이클을 커버합니다.", + "cveTitle": "CVE 취약점 추적", + "cveDesc": "deps.dev와 OSV에서 온디맨드로 보강합니다. 모든 컴포넌트를 CVSS 3.x 점수 및 패치 가능 여부와 교차 검증합니다.", + "cveTag": "deps.dev · OSV · GHSA", + "licenseTitle": "라이선스 리스크 관리", + "licenseDesc": "OSS 라이선스의 SPDX 자동 분류 — 카피레프트, 허용적, 독점 — 의무 규칙, 충돌 감지, 스캔별 NOTICE / SPDX SBOM 출력을 제공합니다.", + "licenseTag": "GPL · LGPL · Apache · MIT · AGPL · MPL", + "riskTitle": "리스크 트렌드 분석", + "riskDesc": "스캔 간 보안 상태를 추적합니다. Critical, High, Medium, Low 심각도 추이를 시각화하고, LLM 프로바이더 설정 시 AI 생성 편차 인사이트를 선택적으로 제공합니다.", + "riskTag": "버전 간 델타", + "transitiveTitle": "전이적 의존성 탐색", + "transitiveDesc": "의존성 그래프 깊숙이 숨은 취약점을 발견합니다. DependencyPath 분석이 지원되는 모든 매니페스트에서 직접·전이적 노출을 추적합니다.", + "transitiveTag": "Maven · npm · PyPI · Go · Cargo · NuGet · RubyGems", + "gitTitle": "Git 네이티브 저장소 연동", + "gitDesc": "Personal Access Token으로 GitHub, GitLab, Bitbucket을 연결합니다. 브랜치 인식 임포트, 버전별 스냅샷, 프로젝트별 스캔 히스토리를 기본 제공합니다.", + "gitTag": "GitHub · GitLab · Bitbucket", + "cliTitle": "CLI & CI/CD 파이프라인 스캔", + "cliDesc": "단일 CLI 명령으로 모든 빌드 파이프라인에 OsWL을 통합합니다. 파싱된 매니페스트 메타데이터만 업로드되며 소스 코드는 빌드 호스트를 벗어나지 않습니다. 프로젝트별 또는 조직 전체 Bearer 토큰 API 키를 지원합니다.", + "cliTag": "매니페스트 전용 업로드" + }, + "platform": { + "label": "라이브 플랫폼", + "title": "보안 &
컴플라이언스 팀을 위해", + "subtitle": "모든 화면은 실행 가능성을 위해 설계되었습니다 — CVE 트리아지부터 조치 추적까지, 컨텍스트 전환 없이 작업할 수 있습니다.", + "tabSc": "보안 센터", + "tabProjects": "프로젝트", + "tabLicense": "라이선스 분석", + "tabRisk": "리스크 트렌드", + "altSc": "OsWL 보안 센터 — CVSS 점수 기반 CVE 트리아지", + "altProjects": "OsWL 프로젝트 대시보드 — 다중 프로젝트 리스크 개요", + "altLicense": "OsWL 라이선스 분석 — 카피레프트 및 컴플라이언스 리스크 추적", + "altRisk": "OsWL 리스크 트렌드 — 스캔 간 보안 상태" + }, + "vuln": { + "label": "취약점 인텔리전스", + "title": "진짜 중요한 것부터
우선순위를 정하세요", + "desc": "OsWL은 악용 가능하고 패치 가능한 취약점을 먼저 표면화합니다. CVSS 점수, 익스플로잇 성숙도, 수정 가능 여부가 deps.dev와 OSV 권고 피드에서 자동으로 보강됩니다.", + "bullet1": "Critical & High 취약점을 패치 대상과 함께 표시", + "bullet2": "수정 버전 표시 — 트리아지 전에 패치된 릴리스 존재 여부 확인", + "bullet3": "전이적 노출을 루트 의존성 경로까지 추적", + "bullet4": "일괄 상태 업데이트 — 한 번에 검토 완료 또는 무시 처리", + "demoTitle": "backend-api · v3.2.0 — 보안 센터" + }, + "table": { + "component": "컴포넌트", + "cveId": "CVE ID", + "cvss": "CVSS", + "severity": "심각도" + }, + "severity": { + "critical": "Critical", + "high": "High", + "medium": "Medium" + }, + "licCard": { + "gpl2Type": "카피레프트 · 강력", + "lgplType": "카피레프트 · 약함", + "apacheType": "허용적", + "mitType": "허용적", + "mplType": "카피레프트 · 파일 단위", + "proprietaryType": "상용", + "restricted": "제한", + "caution": "주의", + "permitted": "허용", + "unknown": "미확인" + }, + "license": { + "label": "라이선스 인텔리전스", + "title": "컴플라이언스
사각지대를 제거하세요", + "desc": "의존성 트리의 모든 SPDX 라이선스를 자동 분류합니다. 카피레프트 의무, 라이선스 충돌, 프로덕션 배포 전 법무 검토가 필요한 컴포넌트를 식별합니다.", + "bullet1": "전체 소스 공개가 필요한 카피레프트 라이선스", + "bullet2": "약한 카피레프트 또는 이중 라이선스 — 법무 검토 권장", + "bullet3": "허용적 라이선스 — 상업적 사용에 안전" + }, + "workflow": { + "label": "워크플로", + "title": "몇 분 안에
스캔을 시작하세요", + "subtitle": "OsWL은 기존 개발 워크플로에 직접 통합됩니다. 에이전트나 무거운 설정이 필요 없습니다.", + "step1Title": "저장소 연결", + "step1Desc": "GitHub, GitLab, Bitbucket Personal Access Token을 등록합니다. OsWL이 저장소와 브랜치를 탐색하고 매니페스트를 임포트합니다 — 소스 코드 업로드는 필요 없습니다.", + "step2Title": "의존성 스캔 실행", + "step2Desc": "OsWL이 lock 파일과 빌드 매니페스트를 파싱하고 전이적 의존성 그래프를 해석한 뒤, 모든 컴포넌트를 deps.dev, OSV, 라이선스 정책과 교차 검증합니다.", + "step3Title": "트리아지 & 조치", + "step3Desc": "보안 센터에서 결과를 검토하고, 심각도별로 필터링하며, 상태를 일괄 업데이트하고, 리스크 트렌드 대시보드에서 스캔 간 보안 상태를 추적합니다.", + "step4Title": "CI/CD 자동화", + "step4Desc": "OsWL CLI로 Pull Request를 게이트합니다. Bearer 토큰 API 키로 Jenkins, GitHub Actions, GitLab CI 또는 커스텀 빌드 서버 등 모든 파이프라인에서 자동 스캔이 가능합니다." + }, + "cta": { + "title": "오픈소스 스택
안전하게 보호하세요", + "sub": "OsWL을 온프레미스에 배포하고 조직의 모든 프로젝트, 팀, 의존성에 대한 완전한 SBOM 가시성을 확보하세요.", + "primary": "사용 방법 보기 →", + "secondary": "기능 살펴보기" + }, + "footer": { + "copyright": "Copyright © 2026 OsWL, Licensed under the MIT License.", + "ossNotices": "오픈소스 고지", + "langLabel": "언어" + } +} diff --git a/landing/index.html b/landing/index.html new file mode 100644 index 0000000..55698ff --- /dev/null +++ b/landing/index.html @@ -0,0 +1,843 @@ + + + + + + OsWL - Enterprise SCA Platform + + + + + + + + + + + + + + + + + +
+
+
+
+ + Enterprise SCA Platform +
+

+ Know Every Vulnerability
in Your Open Source Stack +

+

+ OsWL continuously scans your dependencies for CVEs, license obligations, and transitive risks — + giving security and compliance teams complete SBOM visibility from a single self-hosted platform. +

+ +
+
+ + +
+
+
+
Multi-source
+
GHSA · OSV Advisories
+
+
+
+
7
+
Package Ecosystems
+
+
+
+
On-demand
+
deps.dev Enrichment
+
+
+
+
CVSS 3.x
+
Scoring Supported
+
+
+
+
Git-native
+
GitHub · GitLab · Bitbucket
+
+
+
+ + +
+
+ +

End-to-End Software
Composition Analysis

+

From dependency ingestion to remediation, OsWL covers the full SCA lifecycle with enterprise-grade accuracy and auditability.

+ +
+
+
+ +
+
CVE Vulnerability Tracking
+

On-demand enrichment from deps.dev and OSV. Every component is cross-referenced against CVSS 3.x scores and patch availability.

+ deps.dev · OSV · GHSA +
+ +
+
+ +
+
License Risk Management
+

Automatic SPDX classification of OSS licenses — copyleft, permissive, proprietary — with obligation rules, conflict detection, and NOTICE / SPDX SBOM export per scan.

+ GPL · LGPL · Apache · MIT · AGPL · MPL +
+ +
+
+ +
+
Risk Trend Analysis
+

Track your security posture across scans. Visualize critical, high, medium, and low severity trends over time, with optional AI-generated deviation insights when an LLM provider is configured.

+ Version-over-version delta +
+ +
+
+ +
+
Transitive Dependency Traversal
+

Uncover vulnerabilities hidden deep in your dependency graph. DependencyPath analysis traces both direct and transitive exposure across every supported manifest.

+ Maven · npm · PyPI · Go · Cargo · NuGet · RubyGems +
+ +
+
+ +
+
Git-Native Repository Integration
+

Connect GitHub, GitLab, or Bitbucket via Personal Access Token. Branch-aware imports, per-version snapshots, and per-project scan history out of the box.

+ GitHub · GitLab · Bitbucket +
+ +
+
+ +
+
CLI & CI/CD Pipeline Scanning
+

Integrate OsWL into any build pipeline with a single CLI command. Only parsed manifest metadata is uploaded — no source code leaves your build host. Bearer-token API keys per project or organization-wide.

+ Manifest-only upload +
+
+
+
+ + +
+
+ +

Built for Security &
Compliance Teams

+

Every screen is engineered for actionability — from CVE triage to remediation tracking, no context switching required.

+ +
+ + + + +
+
+ OsWL Security Center — CVE triage with CVSS scores +
+
+ OsWL Projects Dashboard — multi-project risk overview +
+
+ OsWL License Analysis — copyleft and compliance risk tracking +
+
+ OsWL Risk Trend — scan-over-scan security posture +
+
+
+ + +
+
+ + +
+
+ +

Prioritize What
Actually Matters

+

+ OsWL surfaces exploitable, patchable vulnerabilities first. CVSS scores, exploit maturity, and fix availability are enriched automatically from deps.dev and OSV advisory feeds. +

+
+
+
+ Critical & High vulnerabilities flagged with patch targets +
+
+
+ Fix version surfaced — know if a patched release exists before triaging +
+
+
+ Transitive exposure traced to root dependency paths +
+
+
+ Bulk status updates — mark findings reviewed or ignored in one action +
+
+
+
+
+
+
+
+ backend-api · v3.2.0 — Security Center +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentCVE IDCVSSSeverity
jackson-databind
2.13.4.2
CVE-2022-420039.8Critical
spring-webmvc
5.3.27
CVE-2023-208618.6High
commons-io
2.11.0
CVE-2024-220117.5High
logback-classic
1.2.11
CVE-2023-63786.2Medium
guava
31.1-jre
CVE-2023-29765.5Medium
+
+
+ + +
+
+
GPL-2.0-only
Copyleft · Strong
Restricted
+
LGPL-2.1-only
Copyleft · Weak
Caution
+
Apache-2.0
Permissive
Permitted
+
MIT
Permissive
Permitted
+
MPL-2.0
Copyleft · File-level
Caution
+
Proprietary
Commercial
Unknown
+
+
+ +

Eliminate
Compliance Blind Spots

+

+ Automatically classify every SPDX license across your dependency tree. Identify copyleft obligations, license conflicts, and components that need manual legal review before they reach production. +

+
+
+ Restricted + Copyleft licenses requiring full source disclosure +
+
+ Caution + Weak copyleft or dual-licensed — legal review recommended +
+
+ Permitted + Permissive licenses — safe for commercial use +
+
+
+
+ +
+
+ + +
+
+ +

Up and Scanning
in Minutes

+

OsWL integrates directly into your existing development workflow. No agents, no heavyweight setup.

+ +
+
+
01
+

Connect Your Repository

+

Add a GitHub, GitLab, or Bitbucket Personal Access Token. OsWL discovers repositories and branches and imports manifests — no source code upload required.

+
+
+
02
+

Run a Dependency Scan

+

OsWL parses your lock files and build manifests, resolves the transitive dependency graph, and cross-references every component against deps.dev, OSV, and your license policy.

+
+
+
03
+

Triage & Remediate

+

Review findings in the Security Center, filter by severity, bulk-update statuses, and track your risk posture scan-over-scan in the Risk Trend dashboard.

+
+
+
04
+

Automate in CI/CD

+

Gate pull requests with the OsWL CLI. Bearer-token API keys enable automated scanning in any pipeline — Jenkins, GitHub Actions, GitLab CI, or custom build servers.

+
+
+
+
+ + +
+
+

Secure Your
Open Source Stack

+

Deploy OsWL on-premise and gain full SBOM visibility across every project, team, and dependency in your organization.

+ +
+ + + + + + + + diff --git a/landing/landing-i18n.js b/landing/landing-i18n.js new file mode 100644 index 0000000..4e4f601 --- /dev/null +++ b/landing/landing-i18n.js @@ -0,0 +1,93 @@ +(function () { + 'use strict'; + + const STORAGE_KEY = 'oswl-landing-locale'; + const SUPPORTED = ['en', 'ko']; + + function detectLocale() { + const param = new URLSearchParams(location.search).get('lang'); + if (SUPPORTED.includes(param)) return param; + const stored = localStorage.getItem(STORAGE_KEY); + if (SUPPORTED.includes(stored)) return stored; + if ((navigator.language || '').toLowerCase().startsWith('ko')) return 'ko'; + return 'en'; + } + + function get(obj, path) { + return path.split('.').reduce((o, k) => (o && o[k] != null ? o[k] : null), obj); + } + + let currentLocale = 'en'; + + function applyMessages(messages) { + document.querySelectorAll('[data-i18n]').forEach(el => { + const val = get(messages, el.getAttribute('data-i18n')); + if (val != null) el.textContent = val; + }); + + document.querySelectorAll('[data-i18n-html]').forEach(el => { + const val = get(messages, el.getAttribute('data-i18n-html')); + if (val != null) el.innerHTML = val; + }); + + document.querySelectorAll('[data-i18n-attr]').forEach(el => { + el.getAttribute('data-i18n-attr').split(';').forEach(pair => { + const [attr, key] = pair.trim().split(':').map(s => s.trim()); + const val = get(messages, key); + if (val != null && attr) el.setAttribute(attr, val); + }); + }); + + const title = get(messages, 'meta.title'); + if (title) document.title = title; + + document.querySelectorAll('[data-set-lang]').forEach(btn => { + const lang = btn.getAttribute('data-set-lang'); + btn.classList.toggle('active', lang === currentLocale); + btn.setAttribute('aria-current', lang === currentLocale ? 'true' : 'false'); + }); + } + + async function loadAndApply(locale) { + currentLocale = locale; + document.documentElement.lang = locale; + + try { + const res = await fetch(`i18n/${locale}.json`); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + applyMessages(await res.json()); + } catch (e) { + console.warn('[landing-i18n] Failed to load locale:', locale, e); + } finally { + document.documentElement.classList.remove('i18n-pending'); + } + } + + function setLocale(locale) { + if (!SUPPORTED.includes(locale)) return; + localStorage.setItem(STORAGE_KEY, locale); + loadAndApply(locale); + } + + async function init() { + const param = new URLSearchParams(location.search).get('lang'); + if (SUPPORTED.includes(param)) { + localStorage.setItem(STORAGE_KEY, param); + const u = new URL(location.href); + u.searchParams.delete('lang'); + history.replaceState(null, '', u.pathname + u.search + u.hash); + } + + await loadAndApply(detectLocale()); + + document.querySelectorAll('[data-set-lang]').forEach(btn => { + btn.addEventListener('click', () => setLocale(btn.getAttribute('data-set-lang'))); + }); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); diff --git a/src/main/java/com/salkcoding/oswl/auth/security/SecurityConfig.java b/src/main/java/com/salkcoding/oswl/auth/security/SecurityConfig.java index ad392c3..ae083a6 100644 --- a/src/main/java/com/salkcoding/oswl/auth/security/SecurityConfig.java +++ b/src/main/java/com/salkcoding/oswl/auth/security/SecurityConfig.java @@ -99,7 +99,6 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .authorizeHttpRequests(auth -> auth .requestMatchers("/", "/login", "/login/otp-verify", "/login/otp-resend", "/setup", "/error/**").permitAll() .requestMatchers("/css/**", "/js/**", "/icon/**", "/img/**", "/graphic/**", "/scripts/**", "/webjars/**", "/favicon.ico").permitAll() - .requestMatchers("/landing", "/landing/**").permitAll() .requestMatchers("/oss-notices").permitAll() .requestMatchers("/api/scan/**").permitAll() .requestMatchers("/actuator/**").hasRole("SYSTEM_ADMIN") diff --git a/src/main/java/com/salkcoding/oswl/web/config/WebMvcConfig.java b/src/main/java/com/salkcoding/oswl/web/config/WebMvcConfig.java index 195b276..d141ff7 100644 --- a/src/main/java/com/salkcoding/oswl/web/config/WebMvcConfig.java +++ b/src/main/java/com/salkcoding/oswl/web/config/WebMvcConfig.java @@ -47,7 +47,6 @@ public LocaleChangeInterceptor localeChangeInterceptor() { @Override public void addViewControllers(ViewControllerRegistry registry) { - registry.addRedirectViewController("/landing", "/landing/index.html"); registry.addViewController("/oss-notices").setViewName("oss-notices/index"); } diff --git a/src/main/resources/static/scripts/install.ps1 b/src/main/resources/static/scripts/install.ps1 index 7d6b534..764104f 100644 --- a/src/main/resources/static/scripts/install.ps1 +++ b/src/main/resources/static/scripts/install.ps1 @@ -1,5 +1,5 @@ # ───────────────────────────────────────────────────────────────────────────── -# OsWL CLI Installer – Windows PowerShell +# OsWL CLI Installer - Windows PowerShell # Usage (PowerShell as Administrator or scoped to user): # iex ((New-Object System.Net.WebClient).DownloadString('https:///scripts/install.ps1')) # diff --git a/src/main/resources/static/scripts/install.sh b/src/main/resources/static/scripts/install.sh index a030bd3..376f7a8 100644 --- a/src/main/resources/static/scripts/install.sh +++ b/src/main/resources/static/scripts/install.sh @@ -368,7 +368,7 @@ chmod +x "/tmp/${BINARY_NAME}" if [[ -w "${INSTALL_DIR}" ]]; then mv "/tmp/${BINARY_NAME}" "${INSTALL_DIR}/${BINARY_NAME}" else - warn "Insufficient permissions — using sudo." + warn "Insufficient permissions - using sudo." sudo mv "/tmp/${BINARY_NAME}" "${INSTALL_DIR}/${BINARY_NAME}" fi diff --git a/src/test/java/com/salkcoding/oswl/i18n/MessagesI18nConsistencyTest.java b/src/test/java/com/salkcoding/oswl/i18n/MessagesI18nConsistencyTest.java index 6647241..da25c9d 100644 --- a/src/test/java/com/salkcoding/oswl/i18n/MessagesI18nConsistencyTest.java +++ b/src/test/java/com/salkcoding/oswl/i18n/MessagesI18nConsistencyTest.java @@ -27,6 +27,8 @@ class MessagesI18nConsistencyTest { private static final Pattern MESSAGE_KEY = Pattern.compile("^\\s*([A-Za-z0-9_.-]+)\\s*="); private static final Pattern THYMELEAF_KEY = Pattern.compile("#\\{([A-Za-z0-9._|]+)\\}"); private static final Pattern INLINE_SCRIPT_KEY = Pattern.compile("/\\*\\[\\[#\\{([A-Za-z0-9._]+)\\}\\]\\]\\*/"); + /** MessageFormat placeholders like #{0} in fallback strings are not message keys. */ + private static final Pattern VALID_MESSAGE_KEY = Pattern.compile("^[A-Za-z][A-Za-z0-9._]*$"); @Test @DisplayName("messages.properties ↔ messages_ko.properties 키 집합 동일") @@ -86,7 +88,7 @@ private static Set collectReferencedMessageKeys() throws IOException { if (key.contains("|")) { key = key.substring(0, key.indexOf('|')); } - if (!key.isBlank()) { + if (VALID_MESSAGE_KEY.matcher(key).matches()) { keys.add(key); } } diff --git a/src/test/java/com/salkcoding/oswl/service/ComponentDetailServiceTest.java b/src/test/java/com/salkcoding/oswl/service/ComponentDetailServiceTest.java index 7ac8862..3c9ac3e 100644 --- a/src/test/java/com/salkcoding/oswl/service/ComponentDetailServiceTest.java +++ b/src/test/java/com/salkcoding/oswl/service/ComponentDetailServiceTest.java @@ -348,7 +348,7 @@ void createPullRequest_noRepoPath_throws() { @Test @DisplayName("createPR: GITHUB provider이지만 githubToken이 없으면 IllegalStateException을 던진다") void createPullRequest_github_noToken_throws() { - Library lib = Library.builder().id(10L).name("lodash").version("4.17.15").cves(List.of()).build(); + Library lib = libraryForPrBump(); ScanComponent sc = mock(ScanComponent.class); when(sc.getLibrary()).thenReturn(lib); Project project = Project.builder().id(1L).name("P") @@ -360,13 +360,13 @@ void createPullRequest_github_noToken_throws() { CreatePrRequest req = buildCreatePrRequest("main", null, null); assertThatThrownBy(() -> componentDetailService.createPullRequest(1L, 20L, req, 1L, null)) .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("GitHub account"); + .hasMessageContaining("No GitHub account is connected"); } @Test @DisplayName("createPR: GITHUB 성공 경로 — PR URL과 번호가 반환된다") void createPullRequest_github_success() { - Library lib = Library.builder().id(10L).name("lodash").version("4.17.15").cves(List.of()).build(); + Library lib = libraryForPrBump(); ScanComponent sc = mock(ScanComponent.class); when(sc.getLibrary()).thenReturn(lib); Project project = Project.builder().id(1L).name("P") @@ -392,7 +392,7 @@ void createPullRequest_github_success() { @Test @DisplayName("createPR: GITLAB 연결이 없으면 IllegalStateException을 던진다") void createPullRequest_gitlab_noConnection_throws() { - Library lib = Library.builder().id(10L).name("lodash").version("4.17.15").cves(List.of()).build(); + Library lib = libraryForPrBump(); ScanComponent sc = mock(ScanComponent.class); when(sc.getLibrary()).thenReturn(lib); Project project = Project.builder().id(1L).name("P") @@ -412,7 +412,7 @@ void createPullRequest_gitlab_noConnection_throws() { @Test @DisplayName("createPR: GITLAB 성공 경로 — MR URL과 번호가 반환된다") void createPullRequest_gitlab_success() { - Library lib = Library.builder().id(10L).name("lodash").version("4.17.15").cves(List.of()).build(); + Library lib = libraryForPrBump(); ScanComponent sc = mock(ScanComponent.class); when(sc.getLibrary()).thenReturn(lib); Project project = Project.builder().id(1L).name("P") @@ -444,7 +444,7 @@ void createPullRequest_gitlab_success() { @Test @DisplayName("createPR: BITBUCKET 성공 경로 — PR URL과 번호가 반환된다") void createPullRequest_bitbucket_success() { - Library lib = Library.builder().id(10L).name("lodash").version("4.17.15").cves(List.of()).build(); + Library lib = libraryForPrBump(); ScanComponent sc = mock(ScanComponent.class); when(sc.getLibrary()).thenReturn(lib); Project project = Project.builder().id(1L).name("P") @@ -634,6 +634,14 @@ private DeferralRequest buildDeferralRequest(String reason, String expiry, Strin return req; } + private Library libraryForPrBump() { + return Library.builder() + .id(10L).name("lodash").version("4.17.15") + .latestVersion("4.17.21").isLatestVersion(false) + .cves(List.of()) + .build(); + } + private CreatePrRequest buildCreatePrRequest(String targetBranch, List reviewers, String description) { CreatePrRequest req = new CreatePrRequest(); setField(req, "targetBranch", targetBranch); diff --git a/src/test/java/com/salkcoding/oswl/service/manifest/ManifestCollectParityTest.java b/src/test/java/com/salkcoding/oswl/service/manifest/ManifestCollectParityTest.java index ff9ff46..a2cda96 100644 --- a/src/test/java/com/salkcoding/oswl/service/manifest/ManifestCollectParityTest.java +++ b/src/test/java/com/salkcoding/oswl/service/manifest/ManifestCollectParityTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIf; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -48,7 +49,19 @@ static Stream verificationRepos() { new DependencyManifestParserService(new MavenBomVersionResolver()); private final ManifestCollectArchiveService archiveService = new ManifestCollectArchiveService(); - @ParameterizedTest(name = "{0}") + static boolean hasVerificationRepos() { + if (!Files.isDirectory(VERIFY_ROOT)) { + return false; + } + try (Stream dirs = Files.list(VERIFY_ROOT)) { + return dirs.anyMatch(Files::isDirectory); + } catch (Exception e) { + return false; + } + } + + @EnabledIf("hasVerificationRepos") + @ParameterizedTest(name = "{0}") @MethodSource("verificationRepos") @DisplayName("manifest zip parse matches full-tree parse") void manifestZip_matchesFullParse(Path repoDir, @TempDir Path work) throws Exception {