Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Build Linux Installer

on:
push:
branches: [ main ]
workflow_dispatch:

jobs:
build-linux:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 20

- name: Install dependencies
run: npm install

- name: Build Linux AppImage
run: npm run dist:linux

- name: Upload Linux installer
uses: actions/upload-artifact@v3
with:
name: linux-installer
path: dist/*.AppImage
31 changes: 31 additions & 0 deletions .github/workflows/build-macos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Build macOS Installer

on:
push:
branches: [ main ]
workflow_dispatch:

jobs:
build-macos:
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 20

- name: Install dependencies
run: npm install

- name: Build macOS app
run: npm run dist:mac

- name: Upload macOS installer
uses: actions/upload-artifact@v3
with:
name: macos-installer
path: dist/*.dmg
140 changes: 102 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,109 @@
# Soterios
<p align="center">
<img src="assets/icon.png" alt="Soterios" width="128" />
</p>

Soterios is a local-first Windows desktop security and maintenance app built with Electron. It combines ClamAV-backed scanning, Windows security visibility, process inspection, real-time protection, reports, quarantine, password tools, and safe maintenance utilities in one desktop UI.
<h1 align="center">Soterios</h1>

Version: 1.0.2
<p align="center">
<strong>Open-source, local-first desktop security &amp; maintenance toolkit.</strong><br/>
Scan files, inspect processes, audit your system, manage your firewall, check passwords — all without telemetry.
</p>

<p align="center">
<a href="https://github.com/chrisriv10/Soterios/releases/latest"><img src="https://img.shields.io/github/v/release/chrisriv10/Soterios?style=flat-square&label=Latest%20Release" alt="Latest Release" /></a>
<a href="https://github.com/chrisriv10/Soterios/blob/main/build/LICENSE.txt"><img src="https://img.shields.io/badge/License-MIT-blue?style=flat-square" alt="MIT License" /></a>
<a href="https://github.com/chrisriv10/Soterios/releases/latest"><img src="https://img.shields.io/github/downloads/chrisriv10/Soterios/total?style=flat-square&label=Downloads" alt="Downloads" /></a>
</p>

---

## Download & Install

Pick the installer for your operating system from the [latest release](https://github.com/chrisriv10/Soterios/releases/latest):

| Platform | Installer | Notes |
|----------|-----------|-------|
| **Windows** | `Soterios-Setup-x.x.x.exe` | NSIS installer · requires admin for system-level checks |
| **macOS** | `Soterios-x.x.x.dmg` | Drag to Applications · may require Gatekeeper approval |
| **Linux** | `Soterios-x.x.x.AppImage` | `chmod +x` and run · no install needed |

---

## Features

- Security Dashboard with health score, scan status, warnings, ignored warnings, quarantine count, and real-time protection controls
- Virus Scan with quick, full, and custom scans, ClamAV definition updates, progress, cancellation, quarantine, and saved scan reports
- In-app Reports page for browsing, viewing, generating, and deleting scan/security reports
- Process Inspector with risk-first sorting, then highest CPU/RAM impact inside the same risk level
- Windows Security Audit for Defender, UAC, Windows Update, BitLocker, PowerShell policy, and Secure Boot
- Firewall Management for Windows Firewall profile status and rule summaries
- Network Monitor for active connections and interface activity
- Password tools with generator, local strength checks, HIBP k-anonymity password leak checks, and XposedOrNot email breach checks
- Real-Time Protection through a local file system watcher
- Quarantine Management for restoring or permanently deleting isolated files
- Tools & Maintenance scripts for temp cleanup, disk reports, large files, browser cache reports, startup items, network reports, and Windows services reports
- **Security Dashboard** — health score, scan status, warnings, ignored warnings, quarantine count, and real-time protection controls
- **Virus Scan** — quick, full, and custom scans powered by ClamAV with definition updates, progress, cancellation, quarantine, and saved reports
- **Reports** — browse, view, generate, and delete scan and security reports in-app
- **Process Inspector** — risk-first sorting, then highest CPU/RAM impact within the same risk level
- **Windows Security Audit** — Defender, UAC, Windows Update, BitLocker, PowerShell policy, and Secure Boot
- **Firewall Management** — Windows Firewall profile status and rule summaries
- **Network Monitor** — active connections and interface activity
- **Password Tools** — local generator, strength checker, HIBP k-anonymity password leak checks, and XposedOrNot email breach checks
- **Real-Time Protection** — local file-system watcher for live threat detection
- **Quarantine Management** — restore or permanently delete isolated files
- **Tools & Maintenance** — temp cleanup, disk reports, large file finder, browser cache reports, startup items, network reports, and Windows services reports

---

Soterios does not collect telemetry or analytics. Local scanning and system analysis happen on your machine. Network calls occur only when you trigger features that need them, such as ClamAV definition updates, HIBP password checks, or XposedOrNot email breach checks.
## Privacy

## Requirements
Soterios does **not** collect telemetry or analytics. All scanning and system analysis happens locally on your machine. Network calls occur **only** when you explicitly trigger features that require them (ClamAV updates, HIBP checks, XposedOrNot lookups).

- Windows 10 or Windows 11
- Node.js 22 or newer for development/builds
- Administrator rights are requested by the packaged Windows app for system-level checks
- Internet access is optional, but needed for ClamAV definition updates and breach lookups
---

## Setup
## Development Setup

### Prerequisites

- [Node.js](https://nodejs.org/) 22 or newer
- [Git](https://git-scm.com/)

### Clone & Run

```bash
git clone https://github.com/chrisriv10/Soterios.git
cd Soterios
npm install
npm start
```

## Build
### Build Installers

```bash
npm run pack
# Windows (NSIS .exe)
npm run dist:win
```

The Windows installer is written to:
# macOS (.dmg)
npm run dist:mac

```text
dist/Soterios-Setup-1.0.2.exe
# Linux (.AppImage)
npm run dist:linux
```

Built artifacts are output to the `dist/` directory.

---

## Usage

1. Open Soterios.
2. Review the Dashboard for current health, warnings, and real-time protection status.
3. Run a quick, full, or custom scan from Virus Scan.
4. View scan details from Reports without leaving the app.
5. Use Windows Audit for non-firewall Windows security posture checks.
6. Use Firewall Management for firewall profile and rule visibility.
7. Use Process Inspector to review high-risk or high-impact processes.
8. Use Passwords for local generation/strength checks and optional breach lookups.
1. **Dashboard** — review your health score, active warnings, and real-time protection status.
2. **Virus Scan** — run a quick, full, or custom scan.
3. **Reports** — view detailed scan results without leaving the app.
4. **Windows Audit** — check Defender, UAC, BitLocker, and other Windows security settings.
5. **Firewall** — inspect firewall profile status and rules.
6. **Process Inspector** — review high-risk or resource-heavy processes.
7. **Passwords** — generate secure passwords, check strength locally, and optionally look up breaches.

---

## API Notes

- Password leak checks use Have I Been Pwned Pwned Passwords k-anonymity. Only the first 5 SHA-1 hash characters are sent.
- Email breach checks use the free XposedOrNot email API.
| Feature | Service | Privacy |
|---------|---------|---------|
| Password leak checks | [Have I Been Pwned – Pwned Passwords](https://haveibeenpwned.com/Passwords) | Only the first 5 characters of the SHA-1 hash are sent (k-anonymity) |
| Email breach checks | [XposedOrNot](https://xposedornot.com/) | Free public email breach API |

---

## Project Structure

Expand All @@ -76,4 +118,26 @@ src/scripts/ maintenance scripts and registry
src/ui/ shell, CSS, shared JS, and page modules
assets/ Soterios icons and bundled ClamAV files
build/ installer resources
```
tests/ test suites
```

---

## Contributing

Contributions are welcome! To get started:

1. **Fork** the repository.
2. **Create a branch** for your feature or fix: `git checkout -b feature/my-feature`.
3. **Commit** your changes with clear messages.
4. **Push** to your fork and open a **Pull Request**.

Please make sure your changes work locally (`npm start`) before submitting.

---

## License

Soterios is released under the [MIT License](build/LICENSE.txt).

**Copyright © 2026 Chris Rivera**
Binary file modified assets/icon.ico
Binary file not shown.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "soterios",
"version": "1.0.2",
"description": "A local-first desktop toolkit for system maintenance, monitoring, and basic security checks.",
"main": "main.js",
"main": "src/main/main.js",
"author": "Soterios",
"license": "MIT",
"private": true,
Expand Down Expand Up @@ -33,6 +33,7 @@
"appId": "com.soterios.app",
"productName": "Soterios",
"copyright": "Copyright (c) 2026 Chris Rivera",
"icon": "assets/icon.ico",
"directories": {
"output": "dist",
"buildResources": "build"
Expand All @@ -41,7 +42,7 @@
"assets/clamav/**"
],
"files": [
"main.js",
"src/main/main.js",
"preload.js",
"src/**/*",
"assets/**/*",
Expand All @@ -60,7 +61,7 @@
"icon": "assets/icon.ico",
"artifactName": "${productName}-Setup-${version}.${ext}",
"publisherName": "Chris Rivera",
"signAndEditExecutable": false,
"signAndEditExecutable": true,
"requestedExecutionLevel": "requireAdministrator"
},
"nsis": {
Expand Down
24 changes: 12 additions & 12 deletions src/main/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ function logLine(level, message, meta) {
} catch (_) { }
}

function createShieldIcon() {
const iconPath = path.join(__dirname, '..', '..', 'assets', 'icon.ico');
function createIcon() {
const iconPath = path.join(__dirname, '../../assets/icon.ico');
return nativeImage.createFromPath(iconPath);
}

function showNotification(title, body) {
if (!Notification.isSupported()) return;
try {
new Notification({ title, body, icon: createShieldIcon() }).show();
new Notification({ title, body, icon: createIcon() }).show();
} catch (_) {}
}

Expand All @@ -51,7 +51,7 @@ function createWindow() {
minHeight: 640,
backgroundColor: '#0e1117',
title: 'Soterios',
icon: createShieldIcon(),
icon: createIcon(),
webPreferences: {
preload: path.join(__dirname, '../preload/preload.js'),
contextIsolation: true,
Expand All @@ -74,7 +74,7 @@ function createWindow() {

function buildAppMenu() {
const isMac = process.platform === 'darwin';

const aboutHandler = () => {
dialog.showMessageBox(mainWindow, {
type: 'info',
Expand Down Expand Up @@ -130,16 +130,16 @@ app.whenReady().then(async () => {
const heuristicEngine = new HeuristicEngine();
const reputationEngine = new ReputationEngine(db);
const quarantineManager = new QuarantineManager(db);

const scanEngine = new ScanEngine(
db,
eventBus,
clamEngine,
heuristicEngine,
reputationEngine,
db,
eventBus,
clamEngine,
heuristicEngine,
reputationEngine,
quarantineManager
);

const realtimeWatcher = new RealTimeWatcher(db, eventBus, scanEngine);
const processInspector = new ProcessInspector();
const systemAudit = new SystemAudit();
Expand Down
24 changes: 13 additions & 11 deletions src/ui/js/pages/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,12 @@ window.Pages['dashboard'] = {
let isRtpActive = true;

async function loadLastScan() {
const el = container.querySelector('#lastScanTime');
if (!el) return;
const latest = await window.api.invoke('scanReports:latest');
const el = document.getElementById('lastScanTime');
if (!latest) {
el.textContent = 'Never';
return;
}
el.textContent = `${new Date(latest.timestamp).toLocaleString()} (${latest.status})`;
el.textContent = latest
? `${new Date(latest.timestamp).toLocaleString()} (${latest.status})`
: 'Never';
}

async function loadWarnings() {
Expand Down Expand Up @@ -200,7 +199,8 @@ window.Pages['dashboard'] = {
healthDetail.textContent = e.message || 'Unable to calculate health score.';
healthIcon.className = 'status-icon warning';
}
document.getElementById('btnRefreshWarnings').addEventListener('click', loadWarnings);
const btnRefreshWarnings = container.querySelector('#btnRefreshWarnings');
if (btnRefreshWarnings) btnRefreshWarnings.addEventListener('click', loadWarnings);
await loadWarnings();

btnToggleRtp.addEventListener('click', async () => {
Expand All @@ -222,12 +222,13 @@ window.Pages['dashboard'] = {

// Scan buttons
document.getElementById('btnQuickScan').addEventListener('click', async () => {
document.getElementById('lastScanTime').textContent = 'Scanning...';
const lastScanEl = container.querySelector('#lastScanTime');
if (lastScanEl) lastScanEl.textContent = 'Scanning...';
try {
const res = await window.api.invoke('scan:quick');
if (res.error) alert(res.error);
else {
await loadLastScan();
if (container.querySelector('#lastScanTime')) await loadLastScan();
alert(res.canceled ? 'Scan canceled.' : 'Scan completed.');
}
} catch(e) {
Expand All @@ -236,12 +237,13 @@ window.Pages['dashboard'] = {
});

document.getElementById('btnFullScan').addEventListener('click', async () => {
document.getElementById('lastScanTime').textContent = 'Scanning...';
const lastScanEl = container.querySelector('#lastScanTime');
if (lastScanEl) lastScanEl.textContent = 'Scanning...';
try {
const res = await window.api.invoke('scan:full');
if (res.error) alert(res.error);
else {
await loadLastScan();
if (container.querySelector('#lastScanTime')) await loadLastScan();
alert(res.canceled ? 'Scan canceled.' : 'Scan completed.');
}
} catch(e) {
Expand Down