Добавлены исходники пакетов saikyo-*
This commit is contained in:
parent
d11da1fafd
commit
5820108829
121
saikyo-audit-report/bin/saikyo-audit-report
Normal file
121
saikyo-audit-report/bin/saikyo-audit-report
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
OUT_BASE="/var/lib/saikyo-audit-report"
|
||||
TS="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
OUT_DIR="${OUT_BASE}/${TS}"
|
||||
|
||||
mkdir -p "${OUT_DIR}" "${OUT_DIR}/crypto" "${OUT_DIR}/apt" "${OUT_DIR}/system" "${OUT_DIR}/licenses" "${OUT_DIR}/network"
|
||||
chmod 0755 "${OUT_BASE}" "${OUT_DIR}" || true
|
||||
|
||||
{
|
||||
echo "timestamp_utc=${TS}"
|
||||
echo "hostname=$(hostname)"
|
||||
echo "kernel=$(uname -srmo)"
|
||||
echo "arch=$(dpkg --print-architecture 2>/dev/null || true)"
|
||||
} > "${OUT_DIR}/system/summary.env"
|
||||
|
||||
# Saikyo subscription/license status (if present)
|
||||
if command -v saikyo-license >/dev/null 2>&1; then
|
||||
(saikyo-license status 2>&1 || true) > "${OUT_DIR}/system/saikyo-license.status"
|
||||
(saikyo-license verify 2>&1 || true) > "${OUT_DIR}/system/saikyo-license.verify"
|
||||
else
|
||||
echo "saikyo-license not installed" > "${OUT_DIR}/system/saikyo-license.status"
|
||||
fi
|
||||
|
||||
if [ -f /etc/os-release ]; then
|
||||
cp -f /etc/os-release "${OUT_DIR}/system/os-release" || true
|
||||
fi
|
||||
if [ -f /usr/lib/os-release ]; then
|
||||
cp -f /usr/lib/os-release "${OUT_DIR}/system/os-release.lib" || true
|
||||
fi
|
||||
|
||||
# Package inventory
|
||||
(dpkg-query -W -f='${Package}\t${Version}\n' 2>/dev/null || true) > "${OUT_DIR}/packages.tsv"
|
||||
|
||||
# APT sources + keyrings
|
||||
mkdir -p "${OUT_DIR}/apt/sources.list.d" || true
|
||||
if [ -f /etc/apt/sources.list ]; then
|
||||
cp -f /etc/apt/sources.list "${OUT_DIR}/apt/sources.list" || true
|
||||
fi
|
||||
if [ -d /etc/apt/sources.list.d ]; then
|
||||
cp -a /etc/apt/sources.list.d/. "${OUT_DIR}/apt/sources.list.d/" 2>/dev/null || true
|
||||
fi
|
||||
(ls -la /usr/share/keyrings 2>/dev/null || true) > "${OUT_DIR}/apt/keyrings.ls"
|
||||
|
||||
(grep -RhsE '^(deb|deb-src)\s' /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null || true) > "${OUT_DIR}/apt/sources.lines"
|
||||
|
||||
# Hashes for installed Saikyo keyring/list (if present)
|
||||
for f in \
|
||||
/usr/share/keyrings/saikyo-archive-keyring.gpg \
|
||||
/etc/apt/sources.list.d/saikyo-os.list \
|
||||
/etc/os-release \
|
||||
/usr/lib/os-release
|
||||
do
|
||||
if [ -f "$f" ]; then
|
||||
sha256sum "$f" >> "${OUT_DIR}/sha256sum.files" || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Crypto / GOST checks
|
||||
{
|
||||
echo "openssl_version=$(openssl version 2>/dev/null || true)"
|
||||
echo "openssl_engines="
|
||||
openssl engine -t -c 2>/dev/null || true
|
||||
} > "${OUT_DIR}/crypto/openssl-engine.txt"
|
||||
|
||||
if command -v gostsum >/dev/null 2>&1; then
|
||||
echo test | gostsum > "${OUT_DIR}/crypto/gostsum.txt" 2>&1 || true
|
||||
else
|
||||
echo "gostsum not installed" > "${OUT_DIR}/crypto/gostsum.txt"
|
||||
fi
|
||||
|
||||
if command -v lsmod >/dev/null 2>&1; then
|
||||
lsmod | grep -i gost > "${OUT_DIR}/crypto/lsmod-gost.txt" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
(grep -RhsE '(pool\.ntp\.org|ntp\.org|time\.google\.com|time\.windows\.com|geoip|telemetry)' /etc 2>/dev/null || true) > "${OUT_DIR}/network/external-indicators.txt"
|
||||
|
||||
(systemctl list-unit-files 2>/dev/null || true) > "${OUT_DIR}/system/unit-files.txt"
|
||||
|
||||
# Secure Boot / MOK / TPM
|
||||
{
|
||||
echo "mokutil_sb_state:"
|
||||
(mokutil --sb-state 2>/dev/null || true)
|
||||
echo
|
||||
echo "mokutil_list_enrolled:"
|
||||
(mokutil --list-enrolled 2>/dev/null || true)
|
||||
echo
|
||||
echo "tpm_devices:"
|
||||
(ls -la /dev/tpm* 2>/dev/null || true)
|
||||
echo
|
||||
echo "saikyo_mok_der_present:"
|
||||
if [ -f /usr/share/saikyo-os/secure-boot/saikyo-mok.der ]; then
|
||||
sha256sum /usr/share/saikyo-os/secure-boot/saikyo-mok.der || true
|
||||
else
|
||||
echo "missing"
|
||||
fi
|
||||
} > "${OUT_DIR}/system/secure-boot.txt"
|
||||
|
||||
LICENSE_TSV="${OUT_DIR}/licenses/licenses.tsv"
|
||||
PROBLEM_TSV="${OUT_DIR}/licenses/problematic-licenses.tsv"
|
||||
|
||||
echo -e "package\tlicense" > "${LICENSE_TSV}"
|
||||
echo -e "package\tmatched" > "${PROBLEM_TSV}"
|
||||
|
||||
while IFS=$'\t' read -r pkg ver; do
|
||||
[ -n "${pkg}" ] || continue
|
||||
cfile="/usr/share/doc/${pkg}/copyright"
|
||||
lic="UNKNOWN"
|
||||
if [ -f "${cfile}" ]; then
|
||||
lic=$(awk -F': ' 'BEGIN{l=""} $1=="License" && l=="" {l=$2} END{if(l=="") print "UNKNOWN"; else print l}' "${cfile}" 2>/dev/null || echo "UNKNOWN")
|
||||
fi
|
||||
echo -e "${pkg}\t${lic}" >> "${LICENSE_TSV}"
|
||||
case "${lic}" in
|
||||
*SSPL*|*Elastic*|*RSAL*|*Redis*Source*Available*|*Server*Side*Public*License*|*AGPL*)
|
||||
echo -e "${pkg}\t${lic}" >> "${PROBLEM_TSV}"
|
||||
;;
|
||||
esac
|
||||
done < "${OUT_DIR}/packages.tsv"
|
||||
|
||||
echo "Report created: ${OUT_DIR}"
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
saikyo-audit-report (1.0.0) stable; urgency=medium
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- SAIKYO OS <support@saikyo-os.ru> Tue, 07 Jan 2026 00:00:00 +0000
|
||||
|
|
@ -0,0 +1 @@
|
|||
./bin/saikyo-audit-report
|
||||
5
saikyo-audit-report/debian/changelog
Normal file
5
saikyo-audit-report/debian/changelog
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
saikyo-audit-report (1.0.0) stable; urgency=medium
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- SAIKYO OS <support@saikyo-os.ru> Tue, 07 Jan 2026 00:00:00 +0000
|
||||
14
saikyo-audit-report/debian/control
Normal file
14
saikyo-audit-report/debian/control
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Source: saikyo-audit-report
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Maintainer: SAIKYO OS <support@saikyo-os.ru>
|
||||
Build-Depends: debhelper-compat (= 13)
|
||||
Standards-Version: 4.6.2
|
||||
Rules-Requires-Root: no
|
||||
|
||||
Package: saikyo-audit-report
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, bash, coreutils, dpkg, ca-certificates, gnupg, openssl, findutils, grep, sed, gawk
|
||||
Description: Saikyo OS audit and registry report generator
|
||||
Generates a local report directory with package inventory, repositories/keys,
|
||||
OS identity, hashes, and crypto/GOST checks.
|
||||
24
saikyo-audit-report/debian/copyright
Normal file
24
saikyo-audit-report/debian/copyright
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: saikyo-audit-report
|
||||
Source: https://saikyo-os.ru
|
||||
|
||||
Files: *
|
||||
Copyright: 2026 SAIKYO OS <support@saikyo-os.ru>
|
||||
License: MIT
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1
saikyo-audit-report/debian/debhelper-build-stamp
Normal file
1
saikyo-audit-report/debian/debhelper-build-stamp
Normal file
|
|
@ -0,0 +1 @@
|
|||
saikyo-audit-report
|
||||
2
saikyo-audit-report/debian/files
Normal file
2
saikyo-audit-report/debian/files
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
saikyo-audit-report_1.0.0_all.deb admin optional
|
||||
saikyo-audit-report_1.0.0_amd64.buildinfo admin optional
|
||||
1
saikyo-audit-report/debian/install
Normal file
1
saikyo-audit-report/debian/install
Normal file
|
|
@ -0,0 +1 @@
|
|||
bin/saikyo-audit-report usr/sbin/saikyo-audit-report
|
||||
8
saikyo-audit-report/debian/rules
Executable file
8
saikyo-audit-report/debian/rules
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_build:
|
||||
|
||||
override_dh_auto_test:
|
||||
2
saikyo-audit-report/debian/saikyo-audit-report.substvars
Normal file
2
saikyo-audit-report/debian/saikyo-audit-report.substvars
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
misc:Depends=
|
||||
misc:Pre-Depends=
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Package: saikyo-audit-report
|
||||
Version: 1.0.0
|
||||
Architecture: all
|
||||
Maintainer: SAIKYO OS <support@saikyo-os.ru>
|
||||
Installed-Size: 15
|
||||
Depends: bash, coreutils, dpkg, ca-certificates, gnupg, openssl, findutils, grep, sed, gawk
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Description: Saikyo OS audit and registry report generator
|
||||
Generates a local report directory with package inventory, repositories/keys,
|
||||
OS identity, hashes, and crypto/GOST checks.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
c95795694790aeed9d166d5eaf2765da usr/sbin/saikyo-audit-report/saikyo-audit-report
|
||||
a97380b9477d5cbeab831599b3321dfe usr/share/doc/saikyo-audit-report/changelog.gz
|
||||
3a820ad7cb163259d2f91549750a52ed usr/share/doc/saikyo-audit-report/copyright
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
OUT_BASE="/var/lib/saikyo-audit-report"
|
||||
TS="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
OUT_DIR="${OUT_BASE}/${TS}"
|
||||
|
||||
mkdir -p "${OUT_DIR}" "${OUT_DIR}/crypto" "${OUT_DIR}/apt" "${OUT_DIR}/system" "${OUT_DIR}/licenses" "${OUT_DIR}/network"
|
||||
chmod 0755 "${OUT_BASE}" "${OUT_DIR}" || true
|
||||
|
||||
{
|
||||
echo "timestamp_utc=${TS}"
|
||||
echo "hostname=$(hostname)"
|
||||
echo "kernel=$(uname -srmo)"
|
||||
echo "arch=$(dpkg --print-architecture 2>/dev/null || true)"
|
||||
} > "${OUT_DIR}/system/summary.env"
|
||||
|
||||
# Saikyo subscription/license status (if present)
|
||||
if command -v saikyo-license >/dev/null 2>&1; then
|
||||
(saikyo-license status 2>&1 || true) > "${OUT_DIR}/system/saikyo-license.status"
|
||||
(saikyo-license verify 2>&1 || true) > "${OUT_DIR}/system/saikyo-license.verify"
|
||||
else
|
||||
echo "saikyo-license not installed" > "${OUT_DIR}/system/saikyo-license.status"
|
||||
fi
|
||||
|
||||
if [ -f /etc/os-release ]; then
|
||||
cp -f /etc/os-release "${OUT_DIR}/system/os-release" || true
|
||||
fi
|
||||
if [ -f /usr/lib/os-release ]; then
|
||||
cp -f /usr/lib/os-release "${OUT_DIR}/system/os-release.lib" || true
|
||||
fi
|
||||
|
||||
# Package inventory
|
||||
(dpkg-query -W -f='${Package}\t${Version}\n' 2>/dev/null || true) > "${OUT_DIR}/packages.tsv"
|
||||
|
||||
# APT sources + keyrings
|
||||
mkdir -p "${OUT_DIR}/apt/sources.list.d" || true
|
||||
if [ -f /etc/apt/sources.list ]; then
|
||||
cp -f /etc/apt/sources.list "${OUT_DIR}/apt/sources.list" || true
|
||||
fi
|
||||
if [ -d /etc/apt/sources.list.d ]; then
|
||||
cp -a /etc/apt/sources.list.d/. "${OUT_DIR}/apt/sources.list.d/" 2>/dev/null || true
|
||||
fi
|
||||
(ls -la /usr/share/keyrings 2>/dev/null || true) > "${OUT_DIR}/apt/keyrings.ls"
|
||||
|
||||
(grep -RhsE '^(deb|deb-src)\s' /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null || true) > "${OUT_DIR}/apt/sources.lines"
|
||||
|
||||
# Hashes for installed Saikyo keyring/list (if present)
|
||||
for f in \
|
||||
/usr/share/keyrings/saikyo-archive-keyring.gpg \
|
||||
/etc/apt/sources.list.d/saikyo-os.list \
|
||||
/etc/os-release \
|
||||
/usr/lib/os-release
|
||||
do
|
||||
if [ -f "$f" ]; then
|
||||
sha256sum "$f" >> "${OUT_DIR}/sha256sum.files" || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Crypto / GOST checks
|
||||
{
|
||||
echo "openssl_version=$(openssl version 2>/dev/null || true)"
|
||||
echo "openssl_engines="
|
||||
openssl engine -t -c 2>/dev/null || true
|
||||
} > "${OUT_DIR}/crypto/openssl-engine.txt"
|
||||
|
||||
if command -v gostsum >/dev/null 2>&1; then
|
||||
echo test | gostsum > "${OUT_DIR}/crypto/gostsum.txt" 2>&1 || true
|
||||
else
|
||||
echo "gostsum not installed" > "${OUT_DIR}/crypto/gostsum.txt"
|
||||
fi
|
||||
|
||||
if command -v lsmod >/dev/null 2>&1; then
|
||||
lsmod | grep -i gost > "${OUT_DIR}/crypto/lsmod-gost.txt" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
(grep -RhsE '(pool\.ntp\.org|ntp\.org|time\.google\.com|time\.windows\.com|geoip|telemetry)' /etc 2>/dev/null || true) > "${OUT_DIR}/network/external-indicators.txt"
|
||||
|
||||
(systemctl list-unit-files 2>/dev/null || true) > "${OUT_DIR}/system/unit-files.txt"
|
||||
|
||||
# Secure Boot / MOK / TPM
|
||||
{
|
||||
echo "mokutil_sb_state:"
|
||||
(mokutil --sb-state 2>/dev/null || true)
|
||||
echo
|
||||
echo "mokutil_list_enrolled:"
|
||||
(mokutil --list-enrolled 2>/dev/null || true)
|
||||
echo
|
||||
echo "tpm_devices:"
|
||||
(ls -la /dev/tpm* 2>/dev/null || true)
|
||||
echo
|
||||
echo "saikyo_mok_der_present:"
|
||||
if [ -f /usr/share/saikyo-os/secure-boot/saikyo-mok.der ]; then
|
||||
sha256sum /usr/share/saikyo-os/secure-boot/saikyo-mok.der || true
|
||||
else
|
||||
echo "missing"
|
||||
fi
|
||||
} > "${OUT_DIR}/system/secure-boot.txt"
|
||||
|
||||
LICENSE_TSV="${OUT_DIR}/licenses/licenses.tsv"
|
||||
PROBLEM_TSV="${OUT_DIR}/licenses/problematic-licenses.tsv"
|
||||
|
||||
echo -e "package\tlicense" > "${LICENSE_TSV}"
|
||||
echo -e "package\tmatched" > "${PROBLEM_TSV}"
|
||||
|
||||
while IFS=$'\t' read -r pkg ver; do
|
||||
[ -n "${pkg}" ] || continue
|
||||
cfile="/usr/share/doc/${pkg}/copyright"
|
||||
lic="UNKNOWN"
|
||||
if [ -f "${cfile}" ]; then
|
||||
lic=$(awk -F': ' 'BEGIN{l=""} $1=="License" && l=="" {l=$2} END{if(l=="") print "UNKNOWN"; else print l}' "${cfile}" 2>/dev/null || echo "UNKNOWN")
|
||||
fi
|
||||
echo -e "${pkg}\t${lic}" >> "${LICENSE_TSV}"
|
||||
case "${lic}" in
|
||||
*SSPL*|*Elastic*|*RSAL*|*Redis*Source*Available*|*Server*Side*Public*License*|*AGPL*)
|
||||
echo -e "${pkg}\t${lic}" >> "${PROBLEM_TSV}"
|
||||
;;
|
||||
esac
|
||||
done < "${OUT_DIR}/packages.tsv"
|
||||
|
||||
echo "Report created: ${OUT_DIR}"
|
||||
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: saikyo-audit-report
|
||||
Source: https://saikyo-os.ru
|
||||
|
||||
Files: *
|
||||
Copyright: 2026 SAIKYO OS <support@saikyo-os.ru>
|
||||
License: MIT
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1
saikyo-audit-report/debian/source/format
Normal file
1
saikyo-audit-report/debian/source/format
Normal file
|
|
@ -0,0 +1 @@
|
|||
3.0 (native)
|
||||
8
saikyo-av-gui/autostart/saikyo-av-gui.desktop
Normal file
8
saikyo-av-gui/autostart/saikyo-av-gui.desktop
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Saikyo Antivirus (Tray)
|
||||
Comment=Start Saikyo Antivirus tray app
|
||||
Exec=/usr/bin/saikyo-av-gui --tray
|
||||
Icon=saikyo-av
|
||||
Terminal=false
|
||||
X-GNOME-Autostart-enabled=true
|
||||
323
saikyo-av-gui/bin/saikyo-av-admin
Normal file
323
saikyo-av-gui/bin/saikyo-av-admin
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPORTS_DIR="/var/lib/saikyo-av/reports"
|
||||
LOG_DIR="/var/log/saikyo-av"
|
||||
QUAR_DIR="/var/lib/saikyo-av/quarantine"
|
||||
|
||||
mkdir -p "${REPORTS_DIR}" "${LOG_DIR}" "${QUAR_DIR}" || true
|
||||
chmod 0755 /var/lib/saikyo-av "${REPORTS_DIR}" "${LOG_DIR}" 2>/dev/null || true
|
||||
|
||||
cmd="${1:-}"
|
||||
shift || true
|
||||
|
||||
utc_ts() {
|
||||
date -u +%Y-%m-%dT%H:%M:%SZ
|
||||
}
|
||||
|
||||
write_report() {
|
||||
local id="$1"
|
||||
local summary="$2"
|
||||
local severity="$3"
|
||||
local outfile="${REPORTS_DIR}/${id}.json"
|
||||
local created
|
||||
created="$(utc_ts)"
|
||||
|
||||
# shellcheck disable=SC2129
|
||||
{
|
||||
echo "{"
|
||||
echo " \"created_utc\": \"${created}\","
|
||||
echo " \"severity\": \"${severity}\","
|
||||
echo " \"summary\": \"${summary}\","
|
||||
echo " \"details\": {"
|
||||
echo " \"action\": \"${cmd}\","
|
||||
echo " \"argv\": \"$*\""
|
||||
echo " },"
|
||||
echo " \"artifacts\": {},"
|
||||
echo " \"suggested_fixes\": []"
|
||||
echo "}"
|
||||
} > "${outfile}"
|
||||
|
||||
echo "${outfile}"
|
||||
}
|
||||
|
||||
run_to_file() {
|
||||
local out="$1"
|
||||
shift
|
||||
("$@" 2>&1 || true) | sed -e 's/\r$//' > "${out}"
|
||||
}
|
||||
|
||||
json_escape() {
|
||||
# Minimal JSON string escape (no unicode handling needed for our file paths)
|
||||
sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\t/\\t/g' -e 's/\r/\\r/g' -e 's/\n/\\n/g'
|
||||
}
|
||||
|
||||
write_scan_report() {
|
||||
local id="$1"
|
||||
local summary="$2"
|
||||
local severity="$3"
|
||||
local log_file="$4"
|
||||
local detections_tsv="$5"
|
||||
local outfile="${REPORTS_DIR}/${id}.json"
|
||||
local created
|
||||
created="$(utc_ts)"
|
||||
|
||||
local scanned_count infected_count
|
||||
scanned_count="$(grep -E '^Scanned files:' "${log_file}" 2>/dev/null | awk -F': ' '{print $2}' | tail -n 1 || true)"
|
||||
infected_count="$(grep -E '^Infected files:' "${log_file}" 2>/dev/null | awk -F': ' '{print $2}' | tail -n 1 || true)"
|
||||
scanned_count="${scanned_count:-0}"
|
||||
infected_count="${infected_count:-0}"
|
||||
|
||||
{
|
||||
echo "{"
|
||||
echo " \"created_utc\": \"${created}\","
|
||||
echo " \"severity\": \"${severity}\","
|
||||
echo " \"summary\": \"${summary}\","
|
||||
echo " \"details\": {"
|
||||
echo " \"action\": \"${cmd}\","
|
||||
echo " \"argv\": \"$*\","
|
||||
echo " \"scanned_files\": ${scanned_count},"
|
||||
echo " \"infected_files\": ${infected_count},"
|
||||
echo " \"detections\": ["
|
||||
|
||||
if [[ -s "${detections_tsv}" ]]; then
|
||||
first=1
|
||||
while IFS=$'\t' read -r pth sig; do
|
||||
[[ -n "${pth}" ]] || continue
|
||||
pth_esc="$(printf '%s' "${pth}" | json_escape)"
|
||||
sig_esc="$(printf '%s' "${sig}" | json_escape)"
|
||||
if [[ "${first}" -eq 1 ]]; then
|
||||
first=0
|
||||
else
|
||||
echo ","
|
||||
fi
|
||||
printf ' {"path":"%s","name":"%s"}' "${pth_esc}" "${sig_esc}"
|
||||
done < "${detections_tsv}"
|
||||
echo
|
||||
fi
|
||||
|
||||
echo " ]"
|
||||
echo " },"
|
||||
echo " \"artifacts\": {"
|
||||
echo " \"clamav_log\": \"${log_file}\","
|
||||
echo " \"detections_tsv\": \"${detections_tsv}\""
|
||||
echo " },"
|
||||
echo " \"suggested_fixes\": ["
|
||||
echo " {\"id\":\"quarantine\",\"title\":\"Quarantine infected file\",\"description\":\"Move selected file to local quarantine.\",\"requires_consent\":true},"
|
||||
echo " {\"id\":\"delete\",\"title\":\"Delete infected file\",\"description\":\"Delete selected infected file (dangerous).\",\"requires_consent\":true}"
|
||||
echo " ]"
|
||||
echo "}"
|
||||
} > "${outfile}"
|
||||
|
||||
echo "${outfile}"
|
||||
}
|
||||
|
||||
svc_is_enabled() {
|
||||
systemctl is-enabled saikyo-avd.timer 2>/dev/null || true
|
||||
}
|
||||
|
||||
svc_is_active() {
|
||||
systemctl is-active saikyo-avd.timer 2>/dev/null || true
|
||||
}
|
||||
|
||||
case "${cmd}" in
|
||||
health)
|
||||
echo "ok"
|
||||
;;
|
||||
|
||||
status-protection)
|
||||
echo "enabled=$(svc_is_enabled) active=$(svc_is_active)"
|
||||
;;
|
||||
|
||||
run-evidence)
|
||||
outdir="${REPORTS_DIR}/artifacts"
|
||||
mkdir -p "${outdir}" || true
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
evidence_out="${outdir}/saikyo-evidence-${ts}.log"
|
||||
|
||||
if command -v saikyo-evidence >/dev/null 2>&1; then
|
||||
run_to_file "${evidence_out}" saikyo-evidence
|
||||
elif [ -x /usr/bin/saikyo-evidence ]; then
|
||||
run_to_file "${evidence_out}" /usr/bin/saikyo-evidence
|
||||
else
|
||||
echo "saikyo-evidence not found" > "${evidence_out}"
|
||||
fi
|
||||
|
||||
rpt="$(write_report "evidence-${ts}" "Evidence report generated" "info" "${cmd}")"
|
||||
# Append artifacts into report JSON (minimal, without jq dependency).
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"evidence_log\": \"${evidence_out}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
scan)
|
||||
outdir="${REPORTS_DIR}/artifacts"
|
||||
mkdir -p "${outdir}" || true
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
log_file="${outdir}/clamav-scan-${ts}.log"
|
||||
det_tsv="${outdir}/clamav-detections-${ts}.tsv"
|
||||
|
||||
if ! command -v clamscan >/dev/null 2>&1; then
|
||||
echo "clamscan not found (install clamav)" > "${log_file}"
|
||||
rpt="$(write_report "scan-${ts}" "ClamAV scan failed: clamscan not found" "warn" "${cmd}")"
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"clamav_log\": \"${log_file}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Full scan of / with exclusions.
|
||||
# - exclude hidden paths (/.*/)
|
||||
# - exclude virtual/system dirs
|
||||
# - keep a log we can parse
|
||||
(
|
||||
clamscan \
|
||||
-r \
|
||||
--infected \
|
||||
--no-summary \
|
||||
--exclude-dir='^/proc/' \
|
||||
--exclude-dir='^/sys/' \
|
||||
--exclude-dir='^/dev/' \
|
||||
--exclude-dir='^/run/' \
|
||||
--exclude-dir='^/tmp/' \
|
||||
--exclude-dir='/.*/' \
|
||||
/ \
|
||||
2>&1
|
||||
echo
|
||||
clamscan -r --infected --exclude-dir='^/proc/' --exclude-dir='^/sys/' --exclude-dir='^/dev/' --exclude-dir='^/run/' --exclude-dir='^/tmp/' --exclude-dir='/.*/' / --summary 2>/dev/null || true
|
||||
) | sed -e 's/\r$//' > "${log_file}"
|
||||
|
||||
: > "${det_tsv}"
|
||||
# Parse "PATH: SIGNATURE FOUND" lines.
|
||||
grep -E ' FOUND$' "${log_file}" | sed -E 's/: (.+) FOUND$//;t;d' >/dev/null 2>&1 || true
|
||||
while IFS= read -r line; do
|
||||
# Example: /path/file: Eicar-Test-Signature FOUND
|
||||
pth="${line%%:*}"
|
||||
rest="${line#*: }"
|
||||
sig="${rest% FOUND}"
|
||||
printf '%s\t%s\n' "${pth}" "${sig}" >> "${det_tsv}"
|
||||
done < <(grep -E ' FOUND$' "${log_file}" || true)
|
||||
|
||||
infected_count="$(wc -l < "${det_tsv}" 2>/dev/null || echo 0)"
|
||||
severity="info"
|
||||
summary="ClamAV scan completed: no threats"
|
||||
if [[ "${infected_count}" -gt 0 ]]; then
|
||||
severity="bad"
|
||||
summary="ClamAV scan completed: ${infected_count} threat(s) found"
|
||||
fi
|
||||
|
||||
rpt="$(write_scan_report "scan-${ts}" "${summary}" "${severity}" "${log_file}" "${det_tsv}" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
quarantine)
|
||||
target="${1:-}"
|
||||
if [[ -z "${target}" ]]; then
|
||||
echo "missing file path" >&2
|
||||
exit 2
|
||||
fi
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
bn="$(basename -- "${target}" 2>/dev/null || echo file)"
|
||||
dest="${QUAR_DIR}/${ts}-${bn}"
|
||||
if [[ ! -f "${target}" ]]; then
|
||||
rpt="$(write_report "quarantine-${ts}" "Quarantine failed: file not found" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
fi
|
||||
mv -f -- "${target}" "${dest}" 2>>"${LOG_DIR}/saikyo-av-admin.log" || {
|
||||
rpt="$(write_report "quarantine-${ts}" "Quarantine failed: move error" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
}
|
||||
rpt="$(write_report "quarantine-${ts}" "File quarantined" "warn" "${cmd}")"
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"from\": \"${target}\", \"to\": \"${dest}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
delete)
|
||||
target="${1:-}"
|
||||
if [[ -z "${target}" ]]; then
|
||||
echo "missing file path" >&2
|
||||
exit 2
|
||||
fi
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
if [[ ! -e "${target}" ]]; then
|
||||
rpt="$(write_report "delete-${ts}" "Delete failed: path not found" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
fi
|
||||
rm -rf -- "${target}" 2>>"${LOG_DIR}/saikyo-av-admin.log" || {
|
||||
rpt="$(write_report "delete-${ts}" "Delete failed" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
}
|
||||
rpt="$(write_report "delete-${ts}" "File deleted" "warn" "${cmd}")"
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"path\": \"${target}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
run-audit)
|
||||
outdir="${REPORTS_DIR}/artifacts"
|
||||
mkdir -p "${outdir}" || true
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
audit_out="${outdir}/saikyo-audit-report-${ts}.txt"
|
||||
|
||||
if command -v saikyo-audit-report >/dev/null 2>&1; then
|
||||
run_to_file "${audit_out}" saikyo-audit-report
|
||||
else
|
||||
echo "saikyo-audit-report not installed" > "${audit_out}"
|
||||
fi
|
||||
|
||||
rpt="$(write_report "audit-${ts}" "Audit report executed" "info" "${cmd}")"
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"audit_stdout\": \"${audit_out}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
collect-artifacts)
|
||||
outdir="${REPORTS_DIR}/artifacts"
|
||||
mkdir -p "${outdir}" || true
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
collect_out="${outdir}/collect-artifacts-${ts}.log"
|
||||
|
||||
if [ -x /usr/share/saikyo-os/forensics/collect-artifacts.sh ]; then
|
||||
run_to_file "${collect_out}" /usr/share/saikyo-os/forensics/collect-artifacts.sh
|
||||
else
|
||||
echo "collect-artifacts.sh not found" > "${collect_out}"
|
||||
fi
|
||||
|
||||
rpt="$(write_report "collect-${ts}" "Artifacts collection executed" "info" "${cmd}")"
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"collector_log\": \"${collect_out}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
enable-protection)
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
(systemctl daemon-reload 2>&1 || true) >> "${LOG_DIR}/saikyo-av-admin.log" || true
|
||||
if systemctl enable --now saikyo-avd.timer >/dev/null 2>&1; then
|
||||
rpt="$(write_report "enable-${ts}" "Protection enabled" "info" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 0
|
||||
else
|
||||
rpt="$(write_report "enable-${ts}" "Protection enable failed" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
disable-protection)
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
(systemctl daemon-reload 2>&1 || true) >> "${LOG_DIR}/saikyo-av-admin.log" || true
|
||||
if systemctl disable --now saikyo-avd.timer >/dev/null 2>&1; then
|
||||
rpt="$(write_report "disable-${ts}" "Protection disabled" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 0
|
||||
else
|
||||
rpt="$(write_report "disable-${ts}" "Protection disable failed" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: saikyo-av-admin {health|status-protection|run-evidence|run-audit|collect-artifacts|scan|quarantine <file>|delete <file>|enable-protection|disable-protection}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
532
saikyo-av-gui/bin/saikyo-av-gui
Normal file
532
saikyo-av-gui/bin/saikyo-av-gui
Normal file
|
|
@ -0,0 +1,532 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
REPORTS_DIR_DEFAULT = "/var/lib/saikyo-av/reports"
|
||||
|
||||
|
||||
def utc_now() -> str:
|
||||
return datetime.now(tz=timezone.utc).isoformat(timespec="seconds")
|
||||
|
||||
|
||||
def load_reports(reports_dir: pathlib.Path):
|
||||
items = []
|
||||
if not reports_dir.exists():
|
||||
return items
|
||||
for p in sorted(reports_dir.glob("*.json"), key=lambda x: x.stat().st_mtime, reverse=True):
|
||||
try:
|
||||
data = json.loads(p.read_text(encoding="utf-8"))
|
||||
except Exception:
|
||||
data = {}
|
||||
items.append(
|
||||
{
|
||||
"id": p.stem,
|
||||
"path": str(p),
|
||||
"summary": data.get("summary") or "(no summary)",
|
||||
"severity": data.get("severity") or "unknown",
|
||||
"created_utc": data.get("created_utc") or "",
|
||||
"raw": data,
|
||||
}
|
||||
)
|
||||
return items
|
||||
|
||||
|
||||
class ReportsModel(QtCore.QAbstractTableModel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.items = []
|
||||
|
||||
def rowCount(self, parent=QtCore.QModelIndex()):
|
||||
return len(self.items)
|
||||
|
||||
def columnCount(self, parent=QtCore.QModelIndex()):
|
||||
return 3
|
||||
|
||||
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
|
||||
if role != QtCore.Qt.DisplayRole:
|
||||
return None
|
||||
if orientation == QtCore.Qt.Horizontal:
|
||||
return ["Summary", "Severity", "Created (UTC)"][section]
|
||||
return str(section + 1)
|
||||
|
||||
def data(self, index, role=QtCore.Qt.DisplayRole):
|
||||
if not index.isValid():
|
||||
return None
|
||||
item = self.items[index.row()]
|
||||
col = index.column()
|
||||
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
if col == 0:
|
||||
return item.get("summary")
|
||||
if col == 1:
|
||||
return item.get("severity")
|
||||
if col == 2:
|
||||
return item.get("created_utc")
|
||||
|
||||
if role == QtCore.Qt.ForegroundRole and col == 1:
|
||||
sev = (item.get("severity") or "").lower()
|
||||
if sev in {"ok", "info", "low"}:
|
||||
return QtGui.QBrush(QtGui.QColor("#2dd4bf"))
|
||||
if sev in {"medium", "warn", "warning"}:
|
||||
return QtGui.QBrush(QtGui.QColor("#fbbf24"))
|
||||
if sev in {"high", "critical", "bad"}:
|
||||
return QtGui.QBrush(QtGui.QColor("#fb7185"))
|
||||
return QtGui.QBrush(QtGui.QColor("#9bb0d1"))
|
||||
|
||||
return None
|
||||
|
||||
def set_items(self, items):
|
||||
self.beginResetModel()
|
||||
self.items = items
|
||||
self.endResetModel()
|
||||
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow):
|
||||
def __init__(self, reports_dir: pathlib.Path, start_in_tray: bool):
|
||||
super().__init__()
|
||||
self.reports_dir = reports_dir
|
||||
self.start_in_tray = start_in_tray
|
||||
|
||||
self.setWindowTitle("Saikyo Antivirus")
|
||||
self.setWindowIcon(QtGui.QIcon.fromTheme("saikyo-av", QtGui.QIcon.fromTheme("security-high")))
|
||||
self.setMinimumSize(980, 620)
|
||||
|
||||
self.model = ReportsModel()
|
||||
self.table = QtWidgets.QTableView()
|
||||
self.table.setModel(self.model)
|
||||
self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
self.table.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
|
||||
self.table.horizontalHeader().setStretchLastSection(True)
|
||||
self.table.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
|
||||
self.table.horizontalHeader().setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
|
||||
self.table.horizontalHeader().setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
|
||||
|
||||
self.details = QtWidgets.QPlainTextEdit()
|
||||
self.details.setReadOnly(True)
|
||||
self.details.setFont(QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont))
|
||||
|
||||
self.det_list = QtWidgets.QListWidget()
|
||||
self.det_list.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
|
||||
|
||||
self.btn_quarantine = QtWidgets.QPushButton("Quarantine")
|
||||
self.btn_delete = QtWidgets.QPushButton("Delete")
|
||||
for b in [self.btn_quarantine, self.btn_delete]:
|
||||
b.setCursor(QtCore.Qt.PointingHandCursor)
|
||||
b.setMinimumHeight(32)
|
||||
self.btn_delete.setStyleSheet("background:#2b1420;color:#ffd4df;border:1px solid #5b2031;")
|
||||
self.btn_quarantine.setEnabled(False)
|
||||
self.btn_delete.setEnabled(False)
|
||||
|
||||
self.scan_status = QtWidgets.QLabel("")
|
||||
self.scan_status.setStyleSheet("color:#9bb0d1")
|
||||
self.scan_progress = QtWidgets.QProgressBar()
|
||||
self.scan_progress.setRange(0, 0)
|
||||
self.scan_progress.setVisible(False)
|
||||
self.btn_stop_scan = QtWidgets.QPushButton("Stop scan")
|
||||
self.btn_stop_scan.setCursor(QtCore.Qt.PointingHandCursor)
|
||||
self.btn_stop_scan.setMinimumHeight(32)
|
||||
self.btn_stop_scan.setEnabled(False)
|
||||
|
||||
self._scan_proc: QtCore.QProcess | None = None
|
||||
self._scan_timer = QtCore.QElapsedTimer()
|
||||
self._scan_tick = QtCore.QTimer(self)
|
||||
self._scan_tick.setInterval(500)
|
||||
self._scan_tick.timeout.connect(self._update_scan_elapsed)
|
||||
|
||||
self.status = QtWidgets.QLabel("local")
|
||||
self.status.setStyleSheet("color:#9bb0d1")
|
||||
|
||||
btn_evidence = QtWidgets.QPushButton("Evidence")
|
||||
btn_audit = QtWidgets.QPushButton("Audit")
|
||||
btn_collect = QtWidgets.QPushButton("Collect")
|
||||
btn_scan = QtWidgets.QPushButton("SCAN")
|
||||
btn_enable = QtWidgets.QPushButton("Enable")
|
||||
btn_disable = QtWidgets.QPushButton("Disable…")
|
||||
btn_refresh = QtWidgets.QPushButton("Refresh")
|
||||
|
||||
for b in [btn_evidence, btn_audit, btn_collect, btn_scan, btn_enable, btn_disable, btn_refresh]:
|
||||
b.setCursor(QtCore.Qt.PointingHandCursor)
|
||||
b.setMinimumHeight(36)
|
||||
|
||||
btn_scan.setStyleSheet("background:rgba(45,212,191,0.18);border:1px solid rgba(45,212,191,0.35);")
|
||||
|
||||
btn_disable.setStyleSheet("background:#2b1420;color:#ffd4df;border:1px solid #5b2031;")
|
||||
|
||||
left = QtWidgets.QFrame()
|
||||
left.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
ll = QtWidgets.QVBoxLayout(left)
|
||||
ll.setContentsMargins(12, 12, 12, 12)
|
||||
ll.setSpacing(10)
|
||||
|
||||
title = QtWidgets.QLabel("Admin checks")
|
||||
title.setStyleSheet("font-weight:800;font-size:16px")
|
||||
hint = QtWidgets.QLabel("Кнопки запускают проверки с подтверждением (pkexec).")
|
||||
hint.setWordWrap(True)
|
||||
hint.setStyleSheet("color:#9bb0d1;font-size:12px")
|
||||
|
||||
ll.addWidget(title)
|
||||
ll.addWidget(hint)
|
||||
ll.addSpacing(4)
|
||||
ll.addWidget(btn_evidence)
|
||||
ll.addWidget(btn_audit)
|
||||
ll.addWidget(btn_collect)
|
||||
ll.addSpacing(8)
|
||||
ll.addWidget(btn_scan)
|
||||
ll.addSpacing(8)
|
||||
ll.addWidget(btn_enable)
|
||||
ll.addWidget(btn_disable)
|
||||
ll.addStretch(1)
|
||||
ll.addWidget(btn_refresh)
|
||||
ll.addWidget(self.status)
|
||||
|
||||
splitter = QtWidgets.QSplitter()
|
||||
splitter.setOrientation(QtCore.Qt.Vertical)
|
||||
top = QtWidgets.QWidget()
|
||||
tl = QtWidgets.QVBoxLayout(top)
|
||||
tl.setContentsMargins(0, 0, 0, 0)
|
||||
tl.addWidget(self.table)
|
||||
splitter.addWidget(top)
|
||||
details_box = QtWidgets.QWidget()
|
||||
dl = QtWidgets.QVBoxLayout(details_box)
|
||||
dl.setContentsMargins(0, 0, 0, 0)
|
||||
dl.setSpacing(8)
|
||||
|
||||
det_head = QtWidgets.QHBoxLayout()
|
||||
det_title = QtWidgets.QLabel("Detections")
|
||||
det_title.setStyleSheet("font-weight:800;")
|
||||
det_head.addWidget(det_title)
|
||||
det_head.addStretch(1)
|
||||
det_head.addWidget(self.scan_status)
|
||||
det_head.addWidget(self.scan_progress)
|
||||
det_head.addWidget(self.btn_stop_scan)
|
||||
det_head.addWidget(self.btn_quarantine)
|
||||
det_head.addWidget(self.btn_delete)
|
||||
dl.addLayout(det_head)
|
||||
dl.addWidget(self.det_list)
|
||||
|
||||
raw_title = QtWidgets.QLabel("Report JSON")
|
||||
raw_title.setStyleSheet("font-weight:800;")
|
||||
dl.addWidget(raw_title)
|
||||
dl.addWidget(self.details)
|
||||
|
||||
splitter.addWidget(details_box)
|
||||
splitter.setStretchFactor(0, 2)
|
||||
splitter.setStretchFactor(1, 1)
|
||||
|
||||
central = QtWidgets.QWidget()
|
||||
root = QtWidgets.QHBoxLayout(central)
|
||||
root.setContentsMargins(16, 16, 16, 16)
|
||||
root.setSpacing(12)
|
||||
left.setFixedWidth(300)
|
||||
root.addWidget(left)
|
||||
root.addWidget(splitter)
|
||||
self.setCentralWidget(central)
|
||||
|
||||
self.setStyleSheet(
|
||||
"QMainWindow{background:#0b1220;color:#e7eefc;}"
|
||||
"QFrame{background:#0f1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}"
|
||||
"QTableView{background:#0f1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}"
|
||||
"QHeaderView::section{background:#0c1628;color:#9bb0d1;border:none;padding:8px;font-weight:700;}"
|
||||
"QPushButton{background:rgba(96,165,250,0.15);border:1px solid rgba(96,165,250,0.35);border-radius:10px;padding:8px 10px;font-weight:700;}"
|
||||
"QPushButton:hover{background:rgba(96,165,250,0.22);}"
|
||||
"QPlainTextEdit{background:#0c1628;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}"
|
||||
)
|
||||
|
||||
self.table.selectionModel().selectionChanged.connect(self._on_select)
|
||||
|
||||
btn_refresh.clicked.connect(self.refresh)
|
||||
btn_evidence.clicked.connect(lambda: self._run_admin("run-evidence"))
|
||||
btn_audit.clicked.connect(lambda: self._run_admin("run-audit"))
|
||||
btn_collect.clicked.connect(lambda: self._run_admin("collect-artifacts"))
|
||||
btn_scan.clicked.connect(self._scan)
|
||||
btn_enable.clicked.connect(lambda: self._run_admin("enable-protection"))
|
||||
btn_disable.clicked.connect(self._disable)
|
||||
|
||||
self.btn_quarantine.clicked.connect(self._quarantine_selected)
|
||||
self.btn_delete.clicked.connect(self._delete_selected)
|
||||
self.det_list.itemSelectionChanged.connect(self._on_det_select)
|
||||
self.btn_stop_scan.clicked.connect(self._stop_scan)
|
||||
|
||||
self._setup_tray()
|
||||
self.refresh()
|
||||
if self.start_in_tray:
|
||||
QtCore.QTimer.singleShot(50, self.hide)
|
||||
|
||||
def _setup_tray(self):
|
||||
self.tray = QtWidgets.QSystemTrayIcon(self.windowIcon(), self)
|
||||
menu = QtWidgets.QMenu()
|
||||
act_open = menu.addAction("Open")
|
||||
act_refresh = menu.addAction("Refresh")
|
||||
menu.addSeparator()
|
||||
act_quit = menu.addAction("Quit")
|
||||
act_open.triggered.connect(self._show)
|
||||
act_refresh.triggered.connect(self.refresh)
|
||||
act_quit.triggered.connect(QtWidgets.QApplication.quit)
|
||||
self.tray.setContextMenu(menu)
|
||||
self.tray.setToolTip("Saikyo Antivirus")
|
||||
self.tray.activated.connect(lambda r: self._show() if r == QtWidgets.QSystemTrayIcon.Trigger else None)
|
||||
self.tray.show()
|
||||
|
||||
def _show(self):
|
||||
self.showNormal()
|
||||
self.raise_()
|
||||
self.activateWindow()
|
||||
|
||||
def closeEvent(self, event: QtGui.QCloseEvent):
|
||||
event.ignore()
|
||||
self.hide()
|
||||
self.tray.showMessage(
|
||||
"Saikyo Antivirus",
|
||||
"Свернуто в трей. Откройте из значка.",
|
||||
QtWidgets.QSystemTrayIcon.Information,
|
||||
2000,
|
||||
)
|
||||
|
||||
def refresh(self):
|
||||
self.reports_dir.mkdir(parents=True, exist_ok=True)
|
||||
items = load_reports(self.reports_dir)
|
||||
self.model.set_items(items)
|
||||
# systemctl status doesn't require root for is-enabled/is-active.
|
||||
enabled = "unknown"
|
||||
active = "unknown"
|
||||
try:
|
||||
enabled = subprocess.run(
|
||||
["systemctl", "is-enabled", "saikyo-avd.timer"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.strip() or enabled
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
active = subprocess.run(
|
||||
["systemctl", "is-active", "saikyo-avd.timer"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.strip() or active
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.status.setText(f"protection: {enabled}/{active} | reports: {len(items)} | {utc_now()}")
|
||||
if items:
|
||||
self.table.selectRow(0)
|
||||
else:
|
||||
self.details.setPlainText("No reports yet. Use the buttons to generate reports.")
|
||||
|
||||
def _selected(self):
|
||||
rows = self.table.selectionModel().selectedRows()
|
||||
if not rows:
|
||||
return None
|
||||
i = rows[0].row()
|
||||
if i < 0 or i >= len(self.model.items):
|
||||
return None
|
||||
return self.model.items[i]
|
||||
|
||||
def _on_select(self, *_):
|
||||
it = self._selected()
|
||||
if not it:
|
||||
return
|
||||
raw = it.get("raw") or {}
|
||||
self.details.setPlainText(json.dumps(raw, ensure_ascii=False, indent=2))
|
||||
|
||||
self.det_list.clear()
|
||||
self.btn_quarantine.setEnabled(False)
|
||||
self.btn_delete.setEnabled(False)
|
||||
|
||||
dets = []
|
||||
try:
|
||||
dets = (raw.get("details") or {}).get("detections") or []
|
||||
except Exception:
|
||||
dets = []
|
||||
|
||||
for d in dets:
|
||||
p = d.get("path")
|
||||
n = d.get("name")
|
||||
if not p:
|
||||
continue
|
||||
item = QtWidgets.QListWidgetItem(f"{p} [{n or 'unknown'}]")
|
||||
item.setData(QtCore.Qt.UserRole, {"path": p, "name": n})
|
||||
self.det_list.addItem(item)
|
||||
|
||||
def _on_det_select(self):
|
||||
sel = self.det_list.selectedItems()
|
||||
ok = bool(sel)
|
||||
self.btn_quarantine.setEnabled(ok)
|
||||
self.btn_delete.setEnabled(ok)
|
||||
|
||||
def _scan(self):
|
||||
if self._scan_proc is not None:
|
||||
QtWidgets.QMessageBox.information(self, "Scan running", "Scan is already running.")
|
||||
return
|
||||
|
||||
r = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
"Full system scan",
|
||||
"Run full ClamAV scan of '/'? This may take a long time.\n\nProceed?",
|
||||
)
|
||||
if r != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
|
||||
self.scan_progress.setVisible(True)
|
||||
self.btn_stop_scan.setEnabled(True)
|
||||
self.scan_status.setText("Scanning… 0s")
|
||||
self._scan_timer.start()
|
||||
self._scan_tick.start()
|
||||
|
||||
proc = QtCore.QProcess(self)
|
||||
proc.setProgram("pkexec")
|
||||
proc.setArguments(["/usr/sbin/saikyo-av-admin", "scan"])
|
||||
proc.setProcessChannelMode(QtCore.QProcess.MergedChannels)
|
||||
|
||||
buf: list[str] = []
|
||||
|
||||
def on_ready():
|
||||
data = bytes(proc.readAllStandardOutput()).decode("utf-8", errors="replace")
|
||||
if data:
|
||||
buf.append(data)
|
||||
|
||||
def on_finished(exit_code, _status):
|
||||
self._scan_tick.stop()
|
||||
self.scan_progress.setVisible(False)
|
||||
self.btn_stop_scan.setEnabled(False)
|
||||
|
||||
out = "".join(buf).strip()
|
||||
self._scan_proc = None
|
||||
self.scan_status.setText("")
|
||||
|
||||
# Refresh reports list after scan.
|
||||
self.refresh()
|
||||
|
||||
if exit_code == 0:
|
||||
if out:
|
||||
QtWidgets.QMessageBox.information(self, "Scan complete", out)
|
||||
else:
|
||||
msg = out or "Scan failed"
|
||||
QtWidgets.QMessageBox.warning(self, "Scan failed", msg)
|
||||
|
||||
proc.readyReadStandardOutput.connect(on_ready)
|
||||
proc.finished.connect(on_finished)
|
||||
|
||||
self._scan_proc = proc
|
||||
proc.start()
|
||||
|
||||
def _selected_detection_path(self) -> str | None:
|
||||
sel = self.det_list.selectedItems()
|
||||
if not sel:
|
||||
return None
|
||||
data = sel[0].data(QtCore.Qt.UserRole) or {}
|
||||
p = data.get("path")
|
||||
if not p:
|
||||
return None
|
||||
return str(p)
|
||||
|
||||
def _quarantine_selected(self):
|
||||
p = self._selected_detection_path()
|
||||
if not p:
|
||||
return
|
||||
r = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
"Quarantine file",
|
||||
f"Move file to quarantine?\n\n{p}",
|
||||
)
|
||||
if r != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
self._run_admin_with_arg("quarantine", p)
|
||||
|
||||
def _delete_selected(self):
|
||||
p = self._selected_detection_path()
|
||||
if not p:
|
||||
return
|
||||
r1 = QtWidgets.QMessageBox.warning(
|
||||
self,
|
||||
"Delete infected file",
|
||||
f"This will permanently delete the file:\n\n{p}\n\nContinue?",
|
||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
||||
)
|
||||
if r1 != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
r2 = QtWidgets.QMessageBox.warning(
|
||||
self,
|
||||
"Confirm delete",
|
||||
"Are you absolutely sure? This cannot be undone.",
|
||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
||||
)
|
||||
if r2 != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
self._run_admin_with_arg("delete", p)
|
||||
|
||||
def _disable(self):
|
||||
r = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
"Disable protection",
|
||||
"Disable protection requires explicit operator consent. Action will be logged.",
|
||||
)
|
||||
if r != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
self._run_admin("disable-protection")
|
||||
|
||||
def _run_admin(self, action: str):
|
||||
cmd = ["pkexec", "/usr/sbin/saikyo-av-admin", action]
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if proc.returncode == 0:
|
||||
self.refresh()
|
||||
out = (proc.stdout or "").strip()
|
||||
if out:
|
||||
QtWidgets.QMessageBox.information(self, "Done", out)
|
||||
else:
|
||||
msg = (proc.stderr or proc.stdout or "failed").strip()
|
||||
QtWidgets.QMessageBox.warning(self, "Failed", msg)
|
||||
|
||||
def _update_scan_elapsed(self):
|
||||
if self._scan_proc is None:
|
||||
return
|
||||
ms = self._scan_timer.elapsed()
|
||||
self.scan_status.setText(f"Scanning… {int(ms/1000)}s")
|
||||
|
||||
def _stop_scan(self):
|
||||
if self._scan_proc is None:
|
||||
return
|
||||
r = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
"Stop scan",
|
||||
"Stop the running scan?",
|
||||
)
|
||||
if r != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
try:
|
||||
self._scan_proc.kill()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _run_admin_with_arg(self, action: str, arg: str):
|
||||
cmd = ["pkexec", "/usr/sbin/saikyo-av-admin", action, arg]
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if proc.returncode == 0:
|
||||
self.refresh()
|
||||
out = (proc.stdout or "").strip()
|
||||
if out:
|
||||
QtWidgets.QMessageBox.information(self, "Done", out)
|
||||
else:
|
||||
msg = (proc.stderr or proc.stdout or "failed").strip()
|
||||
QtWidgets.QMessageBox.warning(self, "Failed", msg)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(prog="saikyo-av-gui")
|
||||
ap.add_argument("--tray", action="store_true")
|
||||
ap.add_argument("--reports-dir", default=REPORTS_DIR_DEFAULT)
|
||||
args = ap.parse_args()
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
w = MainWindow(pathlib.Path(args.reports_dir), start_in_tray=args.tray)
|
||||
w.show()
|
||||
return app.exec_()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
saikyo-av-gui (0.1.4) stable; urgency=medium
|
||||
|
||||
* Make scan asynchronous: progress indicator and stop button.
|
||||
|
||||
-- SAIKYO OS <support@saikyo-os.ru> Mon, 20 Jan 2026 17:40:00 +0000
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
./bin/saikyo-av-gui
|
||||
./bin/saikyo-av-admin
|
||||
./desktop/saikyo-av-gui.desktop
|
||||
./autostart/saikyo-av-gui.desktop
|
||||
./polkit/org.saikyo.av.admin.policy
|
||||
./icons/saikyo-av.svg
|
||||
5
saikyo-av-gui/debian/changelog
Normal file
5
saikyo-av-gui/debian/changelog
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
saikyo-av-gui (0.1.4) stable; urgency=medium
|
||||
|
||||
* Make scan asynchronous: progress indicator and stop button.
|
||||
|
||||
-- SAIKYO OS <support@saikyo-os.ru> Mon, 20 Jan 2026 17:40:00 +0000
|
||||
14
saikyo-av-gui/debian/control
Normal file
14
saikyo-av-gui/debian/control
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Source: saikyo-av-gui
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Maintainer: SAIKYO OS <support@saikyo-os.ru>
|
||||
Build-Depends: debhelper-compat (= 13)
|
||||
Standards-Version: 4.6.2
|
||||
Rules-Requires-Root: no
|
||||
|
||||
Package: saikyo-av-gui
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, python3, python3-pyqt5, xdg-utils, polkitd, pkexec
|
||||
Recommends: saikyo-audit-report
|
||||
Description: Saikyo Antivirus desktop admin panel (local)
|
||||
Native desktop UI (system tray) for Saikyo Antivirus and registry verification actions.
|
||||
26
saikyo-av-gui/debian/copyright
Normal file
26
saikyo-av-gui/debian/copyright
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: saikyo-av-gui
|
||||
Source: https://saikyo-os.ru/
|
||||
|
||||
Files: *
|
||||
Copyright: 2026 SAIKYO OS
|
||||
License: MIT
|
||||
|
||||
License: MIT
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1
saikyo-av-gui/debian/debhelper-build-stamp
Normal file
1
saikyo-av-gui/debian/debhelper-build-stamp
Normal file
|
|
@ -0,0 +1 @@
|
|||
saikyo-av-gui
|
||||
2
saikyo-av-gui/debian/files
Normal file
2
saikyo-av-gui/debian/files
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
saikyo-av-gui_0.1.4_all.deb admin optional
|
||||
saikyo-av-gui_0.1.4_amd64.buildinfo admin optional
|
||||
6
saikyo-av-gui/debian/install
Normal file
6
saikyo-av-gui/debian/install
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
bin/saikyo-av-gui usr/bin/
|
||||
bin/saikyo-av-admin usr/sbin/
|
||||
desktop/saikyo-av-gui.desktop usr/share/applications/
|
||||
autostart/saikyo-av-gui.desktop etc/xdg/autostart/
|
||||
polkit/org.saikyo.av.admin.policy usr/share/polkit-1/actions/
|
||||
icons/saikyo-av.svg usr/share/icons/hicolor/scalable/apps/
|
||||
16
saikyo-av-gui/debian/rules
Executable file
16
saikyo-av-gui/debian/rules
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_build:
|
||||
|
||||
override_dh_auto_test:
|
||||
|
||||
override_dh_fixperms:
|
||||
dh_fixperms
|
||||
chmod 0755 debian/saikyo-av-gui/usr/bin/saikyo-av-gui
|
||||
chmod 0755 debian/saikyo-av-gui/usr/sbin/saikyo-av-admin
|
||||
chmod 0755 debian/saikyo-av-gui.preinst
|
||||
chmod 0755 debian/saikyo-av-gui.postinst
|
||||
chmod 0755 debian/saikyo-av-gui.prerm
|
||||
1
saikyo-av-gui/debian/saikyo-av-gui.debhelper.log
Normal file
1
saikyo-av-gui/debian/saikyo-av-gui.debhelper.log
Normal file
|
|
@ -0,0 +1 @@
|
|||
dh_fixperms
|
||||
11
saikyo-av-gui/debian/saikyo-av-gui.postinst
Executable file
11
saikyo-av-gui/debian/saikyo-av-gui.postinst
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
mkdir -p /var/lib/saikyo-av/reports /var/log/saikyo-av || true
|
||||
chmod 0755 /var/lib/saikyo-av /var/lib/saikyo-av/reports /var/log/saikyo-av 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
20
saikyo-av-gui/debian/saikyo-av-gui.preinst
Executable file
20
saikyo-av-gui/debian/saikyo-av-gui.preinst
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Fixup for packaging mistake (0.1.0/0.1.1) where dh_install created directories:
|
||||
# /usr/sbin/saikyo-av-admin/saikyo-av-admin
|
||||
# /etc/xdg/autostart/saikyo-av-gui.desktop/saikyo-av-gui.desktop
|
||||
# Remove them before unpacking corrected package.
|
||||
|
||||
if [[ "$1" == "upgrade" || "$1" == "install" ]]; then
|
||||
for d in \
|
||||
/usr/sbin/saikyo-av-admin \
|
||||
/etc/xdg/autostart/saikyo-av-gui.desktop
|
||||
do
|
||||
if [[ -d "$d" ]]; then
|
||||
rm -rf "$d" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
exit 0
|
||||
4
saikyo-av-gui/debian/saikyo-av-gui.prerm
Executable file
4
saikyo-av-gui/debian/saikyo-av-gui.prerm
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
exit 0
|
||||
2
saikyo-av-gui/debian/saikyo-av-gui.substvars
Normal file
2
saikyo-av-gui/debian/saikyo-av-gui.substvars
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
misc:Depends=
|
||||
misc:Pre-Depends=
|
||||
1
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/conffiles
Normal file
1
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/conffiles
Normal file
|
|
@ -0,0 +1 @@
|
|||
/etc/xdg/autostart/saikyo-av-gui.desktop
|
||||
11
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/control
Normal file
11
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/control
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
Package: saikyo-av-gui
|
||||
Version: 0.1.4
|
||||
Architecture: all
|
||||
Maintainer: SAIKYO OS <support@saikyo-os.ru>
|
||||
Installed-Size: 59
|
||||
Depends: python3, python3-pyqt5, xdg-utils, polkitd, pkexec
|
||||
Recommends: saikyo-audit-report
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Description: Saikyo Antivirus desktop admin panel (local)
|
||||
Native desktop UI (system tray) for Saikyo Antivirus and registry verification actions.
|
||||
7
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/md5sums
Normal file
7
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/md5sums
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
df448873f75684e84718315a3036d104 usr/bin/saikyo-av-gui
|
||||
2cf2d7c3465f90e05e9e3c34a8d1ede2 usr/sbin/saikyo-av-admin
|
||||
5bdebcd69cfeabc37b34f22edb72c851 usr/share/applications/saikyo-av-gui.desktop
|
||||
390713466d3468f6733a8e1b3f20a231 usr/share/doc/saikyo-av-gui/changelog.gz
|
||||
7b9b58e66ef873f2c38b3a148fa3e513 usr/share/doc/saikyo-av-gui/copyright
|
||||
5161807a723b0a180d3d0e29fee68753 usr/share/icons/hicolor/scalable/apps/saikyo-av.svg
|
||||
1522d63a46b6d96da78f1742361b6b8b usr/share/polkit-1/actions/org.saikyo.av.admin.policy
|
||||
11
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/postinst
Executable file
11
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/postinst
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
mkdir -p /var/lib/saikyo-av/reports /var/log/saikyo-av || true
|
||||
chmod 0755 /var/lib/saikyo-av /var/lib/saikyo-av/reports /var/log/saikyo-av 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
20
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/preinst
Executable file
20
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/preinst
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Fixup for packaging mistake (0.1.0/0.1.1) where dh_install created directories:
|
||||
# /usr/sbin/saikyo-av-admin/saikyo-av-admin
|
||||
# /etc/xdg/autostart/saikyo-av-gui.desktop/saikyo-av-gui.desktop
|
||||
# Remove them before unpacking corrected package.
|
||||
|
||||
if [[ "$1" == "upgrade" || "$1" == "install" ]]; then
|
||||
for d in \
|
||||
/usr/sbin/saikyo-av-admin \
|
||||
/etc/xdg/autostart/saikyo-av-gui.desktop
|
||||
do
|
||||
if [[ -d "$d" ]]; then
|
||||
rm -rf "$d" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
exit 0
|
||||
4
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/prerm
Executable file
4
saikyo-av-gui/debian/saikyo-av-gui/DEBIAN/prerm
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Saikyo Antivirus (Tray)
|
||||
Comment=Start Saikyo Antivirus tray app
|
||||
Exec=/usr/bin/saikyo-av-gui --tray
|
||||
Icon=saikyo-av
|
||||
Terminal=false
|
||||
X-GNOME-Autostart-enabled=true
|
||||
532
saikyo-av-gui/debian/saikyo-av-gui/usr/bin/saikyo-av-gui
Executable file
532
saikyo-av-gui/debian/saikyo-av-gui/usr/bin/saikyo-av-gui
Executable file
|
|
@ -0,0 +1,532 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
REPORTS_DIR_DEFAULT = "/var/lib/saikyo-av/reports"
|
||||
|
||||
|
||||
def utc_now() -> str:
|
||||
return datetime.now(tz=timezone.utc).isoformat(timespec="seconds")
|
||||
|
||||
|
||||
def load_reports(reports_dir: pathlib.Path):
|
||||
items = []
|
||||
if not reports_dir.exists():
|
||||
return items
|
||||
for p in sorted(reports_dir.glob("*.json"), key=lambda x: x.stat().st_mtime, reverse=True):
|
||||
try:
|
||||
data = json.loads(p.read_text(encoding="utf-8"))
|
||||
except Exception:
|
||||
data = {}
|
||||
items.append(
|
||||
{
|
||||
"id": p.stem,
|
||||
"path": str(p),
|
||||
"summary": data.get("summary") or "(no summary)",
|
||||
"severity": data.get("severity") or "unknown",
|
||||
"created_utc": data.get("created_utc") or "",
|
||||
"raw": data,
|
||||
}
|
||||
)
|
||||
return items
|
||||
|
||||
|
||||
class ReportsModel(QtCore.QAbstractTableModel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.items = []
|
||||
|
||||
def rowCount(self, parent=QtCore.QModelIndex()):
|
||||
return len(self.items)
|
||||
|
||||
def columnCount(self, parent=QtCore.QModelIndex()):
|
||||
return 3
|
||||
|
||||
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
|
||||
if role != QtCore.Qt.DisplayRole:
|
||||
return None
|
||||
if orientation == QtCore.Qt.Horizontal:
|
||||
return ["Summary", "Severity", "Created (UTC)"][section]
|
||||
return str(section + 1)
|
||||
|
||||
def data(self, index, role=QtCore.Qt.DisplayRole):
|
||||
if not index.isValid():
|
||||
return None
|
||||
item = self.items[index.row()]
|
||||
col = index.column()
|
||||
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
if col == 0:
|
||||
return item.get("summary")
|
||||
if col == 1:
|
||||
return item.get("severity")
|
||||
if col == 2:
|
||||
return item.get("created_utc")
|
||||
|
||||
if role == QtCore.Qt.ForegroundRole and col == 1:
|
||||
sev = (item.get("severity") or "").lower()
|
||||
if sev in {"ok", "info", "low"}:
|
||||
return QtGui.QBrush(QtGui.QColor("#2dd4bf"))
|
||||
if sev in {"medium", "warn", "warning"}:
|
||||
return QtGui.QBrush(QtGui.QColor("#fbbf24"))
|
||||
if sev in {"high", "critical", "bad"}:
|
||||
return QtGui.QBrush(QtGui.QColor("#fb7185"))
|
||||
return QtGui.QBrush(QtGui.QColor("#9bb0d1"))
|
||||
|
||||
return None
|
||||
|
||||
def set_items(self, items):
|
||||
self.beginResetModel()
|
||||
self.items = items
|
||||
self.endResetModel()
|
||||
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow):
|
||||
def __init__(self, reports_dir: pathlib.Path, start_in_tray: bool):
|
||||
super().__init__()
|
||||
self.reports_dir = reports_dir
|
||||
self.start_in_tray = start_in_tray
|
||||
|
||||
self.setWindowTitle("Saikyo Antivirus")
|
||||
self.setWindowIcon(QtGui.QIcon.fromTheme("saikyo-av", QtGui.QIcon.fromTheme("security-high")))
|
||||
self.setMinimumSize(980, 620)
|
||||
|
||||
self.model = ReportsModel()
|
||||
self.table = QtWidgets.QTableView()
|
||||
self.table.setModel(self.model)
|
||||
self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
self.table.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
|
||||
self.table.horizontalHeader().setStretchLastSection(True)
|
||||
self.table.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
|
||||
self.table.horizontalHeader().setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
|
||||
self.table.horizontalHeader().setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
|
||||
|
||||
self.details = QtWidgets.QPlainTextEdit()
|
||||
self.details.setReadOnly(True)
|
||||
self.details.setFont(QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont))
|
||||
|
||||
self.det_list = QtWidgets.QListWidget()
|
||||
self.det_list.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
|
||||
|
||||
self.btn_quarantine = QtWidgets.QPushButton("Quarantine")
|
||||
self.btn_delete = QtWidgets.QPushButton("Delete")
|
||||
for b in [self.btn_quarantine, self.btn_delete]:
|
||||
b.setCursor(QtCore.Qt.PointingHandCursor)
|
||||
b.setMinimumHeight(32)
|
||||
self.btn_delete.setStyleSheet("background:#2b1420;color:#ffd4df;border:1px solid #5b2031;")
|
||||
self.btn_quarantine.setEnabled(False)
|
||||
self.btn_delete.setEnabled(False)
|
||||
|
||||
self.scan_status = QtWidgets.QLabel("")
|
||||
self.scan_status.setStyleSheet("color:#9bb0d1")
|
||||
self.scan_progress = QtWidgets.QProgressBar()
|
||||
self.scan_progress.setRange(0, 0)
|
||||
self.scan_progress.setVisible(False)
|
||||
self.btn_stop_scan = QtWidgets.QPushButton("Stop scan")
|
||||
self.btn_stop_scan.setCursor(QtCore.Qt.PointingHandCursor)
|
||||
self.btn_stop_scan.setMinimumHeight(32)
|
||||
self.btn_stop_scan.setEnabled(False)
|
||||
|
||||
self._scan_proc: QtCore.QProcess | None = None
|
||||
self._scan_timer = QtCore.QElapsedTimer()
|
||||
self._scan_tick = QtCore.QTimer(self)
|
||||
self._scan_tick.setInterval(500)
|
||||
self._scan_tick.timeout.connect(self._update_scan_elapsed)
|
||||
|
||||
self.status = QtWidgets.QLabel("local")
|
||||
self.status.setStyleSheet("color:#9bb0d1")
|
||||
|
||||
btn_evidence = QtWidgets.QPushButton("Evidence")
|
||||
btn_audit = QtWidgets.QPushButton("Audit")
|
||||
btn_collect = QtWidgets.QPushButton("Collect")
|
||||
btn_scan = QtWidgets.QPushButton("SCAN")
|
||||
btn_enable = QtWidgets.QPushButton("Enable")
|
||||
btn_disable = QtWidgets.QPushButton("Disable…")
|
||||
btn_refresh = QtWidgets.QPushButton("Refresh")
|
||||
|
||||
for b in [btn_evidence, btn_audit, btn_collect, btn_scan, btn_enable, btn_disable, btn_refresh]:
|
||||
b.setCursor(QtCore.Qt.PointingHandCursor)
|
||||
b.setMinimumHeight(36)
|
||||
|
||||
btn_scan.setStyleSheet("background:rgba(45,212,191,0.18);border:1px solid rgba(45,212,191,0.35);")
|
||||
|
||||
btn_disable.setStyleSheet("background:#2b1420;color:#ffd4df;border:1px solid #5b2031;")
|
||||
|
||||
left = QtWidgets.QFrame()
|
||||
left.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
ll = QtWidgets.QVBoxLayout(left)
|
||||
ll.setContentsMargins(12, 12, 12, 12)
|
||||
ll.setSpacing(10)
|
||||
|
||||
title = QtWidgets.QLabel("Admin checks")
|
||||
title.setStyleSheet("font-weight:800;font-size:16px")
|
||||
hint = QtWidgets.QLabel("Кнопки запускают проверки с подтверждением (pkexec).")
|
||||
hint.setWordWrap(True)
|
||||
hint.setStyleSheet("color:#9bb0d1;font-size:12px")
|
||||
|
||||
ll.addWidget(title)
|
||||
ll.addWidget(hint)
|
||||
ll.addSpacing(4)
|
||||
ll.addWidget(btn_evidence)
|
||||
ll.addWidget(btn_audit)
|
||||
ll.addWidget(btn_collect)
|
||||
ll.addSpacing(8)
|
||||
ll.addWidget(btn_scan)
|
||||
ll.addSpacing(8)
|
||||
ll.addWidget(btn_enable)
|
||||
ll.addWidget(btn_disable)
|
||||
ll.addStretch(1)
|
||||
ll.addWidget(btn_refresh)
|
||||
ll.addWidget(self.status)
|
||||
|
||||
splitter = QtWidgets.QSplitter()
|
||||
splitter.setOrientation(QtCore.Qt.Vertical)
|
||||
top = QtWidgets.QWidget()
|
||||
tl = QtWidgets.QVBoxLayout(top)
|
||||
tl.setContentsMargins(0, 0, 0, 0)
|
||||
tl.addWidget(self.table)
|
||||
splitter.addWidget(top)
|
||||
details_box = QtWidgets.QWidget()
|
||||
dl = QtWidgets.QVBoxLayout(details_box)
|
||||
dl.setContentsMargins(0, 0, 0, 0)
|
||||
dl.setSpacing(8)
|
||||
|
||||
det_head = QtWidgets.QHBoxLayout()
|
||||
det_title = QtWidgets.QLabel("Detections")
|
||||
det_title.setStyleSheet("font-weight:800;")
|
||||
det_head.addWidget(det_title)
|
||||
det_head.addStretch(1)
|
||||
det_head.addWidget(self.scan_status)
|
||||
det_head.addWidget(self.scan_progress)
|
||||
det_head.addWidget(self.btn_stop_scan)
|
||||
det_head.addWidget(self.btn_quarantine)
|
||||
det_head.addWidget(self.btn_delete)
|
||||
dl.addLayout(det_head)
|
||||
dl.addWidget(self.det_list)
|
||||
|
||||
raw_title = QtWidgets.QLabel("Report JSON")
|
||||
raw_title.setStyleSheet("font-weight:800;")
|
||||
dl.addWidget(raw_title)
|
||||
dl.addWidget(self.details)
|
||||
|
||||
splitter.addWidget(details_box)
|
||||
splitter.setStretchFactor(0, 2)
|
||||
splitter.setStretchFactor(1, 1)
|
||||
|
||||
central = QtWidgets.QWidget()
|
||||
root = QtWidgets.QHBoxLayout(central)
|
||||
root.setContentsMargins(16, 16, 16, 16)
|
||||
root.setSpacing(12)
|
||||
left.setFixedWidth(300)
|
||||
root.addWidget(left)
|
||||
root.addWidget(splitter)
|
||||
self.setCentralWidget(central)
|
||||
|
||||
self.setStyleSheet(
|
||||
"QMainWindow{background:#0b1220;color:#e7eefc;}"
|
||||
"QFrame{background:#0f1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}"
|
||||
"QTableView{background:#0f1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}"
|
||||
"QHeaderView::section{background:#0c1628;color:#9bb0d1;border:none;padding:8px;font-weight:700;}"
|
||||
"QPushButton{background:rgba(96,165,250,0.15);border:1px solid rgba(96,165,250,0.35);border-radius:10px;padding:8px 10px;font-weight:700;}"
|
||||
"QPushButton:hover{background:rgba(96,165,250,0.22);}"
|
||||
"QPlainTextEdit{background:#0c1628;border:1px solid rgba(255,255,255,0.08);border-radius:14px;}"
|
||||
)
|
||||
|
||||
self.table.selectionModel().selectionChanged.connect(self._on_select)
|
||||
|
||||
btn_refresh.clicked.connect(self.refresh)
|
||||
btn_evidence.clicked.connect(lambda: self._run_admin("run-evidence"))
|
||||
btn_audit.clicked.connect(lambda: self._run_admin("run-audit"))
|
||||
btn_collect.clicked.connect(lambda: self._run_admin("collect-artifacts"))
|
||||
btn_scan.clicked.connect(self._scan)
|
||||
btn_enable.clicked.connect(lambda: self._run_admin("enable-protection"))
|
||||
btn_disable.clicked.connect(self._disable)
|
||||
|
||||
self.btn_quarantine.clicked.connect(self._quarantine_selected)
|
||||
self.btn_delete.clicked.connect(self._delete_selected)
|
||||
self.det_list.itemSelectionChanged.connect(self._on_det_select)
|
||||
self.btn_stop_scan.clicked.connect(self._stop_scan)
|
||||
|
||||
self._setup_tray()
|
||||
self.refresh()
|
||||
if self.start_in_tray:
|
||||
QtCore.QTimer.singleShot(50, self.hide)
|
||||
|
||||
def _setup_tray(self):
|
||||
self.tray = QtWidgets.QSystemTrayIcon(self.windowIcon(), self)
|
||||
menu = QtWidgets.QMenu()
|
||||
act_open = menu.addAction("Open")
|
||||
act_refresh = menu.addAction("Refresh")
|
||||
menu.addSeparator()
|
||||
act_quit = menu.addAction("Quit")
|
||||
act_open.triggered.connect(self._show)
|
||||
act_refresh.triggered.connect(self.refresh)
|
||||
act_quit.triggered.connect(QtWidgets.QApplication.quit)
|
||||
self.tray.setContextMenu(menu)
|
||||
self.tray.setToolTip("Saikyo Antivirus")
|
||||
self.tray.activated.connect(lambda r: self._show() if r == QtWidgets.QSystemTrayIcon.Trigger else None)
|
||||
self.tray.show()
|
||||
|
||||
def _show(self):
|
||||
self.showNormal()
|
||||
self.raise_()
|
||||
self.activateWindow()
|
||||
|
||||
def closeEvent(self, event: QtGui.QCloseEvent):
|
||||
event.ignore()
|
||||
self.hide()
|
||||
self.tray.showMessage(
|
||||
"Saikyo Antivirus",
|
||||
"Свернуто в трей. Откройте из значка.",
|
||||
QtWidgets.QSystemTrayIcon.Information,
|
||||
2000,
|
||||
)
|
||||
|
||||
def refresh(self):
|
||||
self.reports_dir.mkdir(parents=True, exist_ok=True)
|
||||
items = load_reports(self.reports_dir)
|
||||
self.model.set_items(items)
|
||||
# systemctl status doesn't require root for is-enabled/is-active.
|
||||
enabled = "unknown"
|
||||
active = "unknown"
|
||||
try:
|
||||
enabled = subprocess.run(
|
||||
["systemctl", "is-enabled", "saikyo-avd.timer"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.strip() or enabled
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
active = subprocess.run(
|
||||
["systemctl", "is-active", "saikyo-avd.timer"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.strip() or active
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.status.setText(f"protection: {enabled}/{active} | reports: {len(items)} | {utc_now()}")
|
||||
if items:
|
||||
self.table.selectRow(0)
|
||||
else:
|
||||
self.details.setPlainText("No reports yet. Use the buttons to generate reports.")
|
||||
|
||||
def _selected(self):
|
||||
rows = self.table.selectionModel().selectedRows()
|
||||
if not rows:
|
||||
return None
|
||||
i = rows[0].row()
|
||||
if i < 0 or i >= len(self.model.items):
|
||||
return None
|
||||
return self.model.items[i]
|
||||
|
||||
def _on_select(self, *_):
|
||||
it = self._selected()
|
||||
if not it:
|
||||
return
|
||||
raw = it.get("raw") or {}
|
||||
self.details.setPlainText(json.dumps(raw, ensure_ascii=False, indent=2))
|
||||
|
||||
self.det_list.clear()
|
||||
self.btn_quarantine.setEnabled(False)
|
||||
self.btn_delete.setEnabled(False)
|
||||
|
||||
dets = []
|
||||
try:
|
||||
dets = (raw.get("details") or {}).get("detections") or []
|
||||
except Exception:
|
||||
dets = []
|
||||
|
||||
for d in dets:
|
||||
p = d.get("path")
|
||||
n = d.get("name")
|
||||
if not p:
|
||||
continue
|
||||
item = QtWidgets.QListWidgetItem(f"{p} [{n or 'unknown'}]")
|
||||
item.setData(QtCore.Qt.UserRole, {"path": p, "name": n})
|
||||
self.det_list.addItem(item)
|
||||
|
||||
def _on_det_select(self):
|
||||
sel = self.det_list.selectedItems()
|
||||
ok = bool(sel)
|
||||
self.btn_quarantine.setEnabled(ok)
|
||||
self.btn_delete.setEnabled(ok)
|
||||
|
||||
def _scan(self):
|
||||
if self._scan_proc is not None:
|
||||
QtWidgets.QMessageBox.information(self, "Scan running", "Scan is already running.")
|
||||
return
|
||||
|
||||
r = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
"Full system scan",
|
||||
"Run full ClamAV scan of '/'? This may take a long time.\n\nProceed?",
|
||||
)
|
||||
if r != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
|
||||
self.scan_progress.setVisible(True)
|
||||
self.btn_stop_scan.setEnabled(True)
|
||||
self.scan_status.setText("Scanning… 0s")
|
||||
self._scan_timer.start()
|
||||
self._scan_tick.start()
|
||||
|
||||
proc = QtCore.QProcess(self)
|
||||
proc.setProgram("pkexec")
|
||||
proc.setArguments(["/usr/sbin/saikyo-av-admin", "scan"])
|
||||
proc.setProcessChannelMode(QtCore.QProcess.MergedChannels)
|
||||
|
||||
buf: list[str] = []
|
||||
|
||||
def on_ready():
|
||||
data = bytes(proc.readAllStandardOutput()).decode("utf-8", errors="replace")
|
||||
if data:
|
||||
buf.append(data)
|
||||
|
||||
def on_finished(exit_code, _status):
|
||||
self._scan_tick.stop()
|
||||
self.scan_progress.setVisible(False)
|
||||
self.btn_stop_scan.setEnabled(False)
|
||||
|
||||
out = "".join(buf).strip()
|
||||
self._scan_proc = None
|
||||
self.scan_status.setText("")
|
||||
|
||||
# Refresh reports list after scan.
|
||||
self.refresh()
|
||||
|
||||
if exit_code == 0:
|
||||
if out:
|
||||
QtWidgets.QMessageBox.information(self, "Scan complete", out)
|
||||
else:
|
||||
msg = out or "Scan failed"
|
||||
QtWidgets.QMessageBox.warning(self, "Scan failed", msg)
|
||||
|
||||
proc.readyReadStandardOutput.connect(on_ready)
|
||||
proc.finished.connect(on_finished)
|
||||
|
||||
self._scan_proc = proc
|
||||
proc.start()
|
||||
|
||||
def _selected_detection_path(self) -> str | None:
|
||||
sel = self.det_list.selectedItems()
|
||||
if not sel:
|
||||
return None
|
||||
data = sel[0].data(QtCore.Qt.UserRole) or {}
|
||||
p = data.get("path")
|
||||
if not p:
|
||||
return None
|
||||
return str(p)
|
||||
|
||||
def _quarantine_selected(self):
|
||||
p = self._selected_detection_path()
|
||||
if not p:
|
||||
return
|
||||
r = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
"Quarantine file",
|
||||
f"Move file to quarantine?\n\n{p}",
|
||||
)
|
||||
if r != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
self._run_admin_with_arg("quarantine", p)
|
||||
|
||||
def _delete_selected(self):
|
||||
p = self._selected_detection_path()
|
||||
if not p:
|
||||
return
|
||||
r1 = QtWidgets.QMessageBox.warning(
|
||||
self,
|
||||
"Delete infected file",
|
||||
f"This will permanently delete the file:\n\n{p}\n\nContinue?",
|
||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
||||
)
|
||||
if r1 != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
r2 = QtWidgets.QMessageBox.warning(
|
||||
self,
|
||||
"Confirm delete",
|
||||
"Are you absolutely sure? This cannot be undone.",
|
||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
||||
)
|
||||
if r2 != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
self._run_admin_with_arg("delete", p)
|
||||
|
||||
def _disable(self):
|
||||
r = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
"Disable protection",
|
||||
"Disable protection requires explicit operator consent. Action will be logged.",
|
||||
)
|
||||
if r != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
self._run_admin("disable-protection")
|
||||
|
||||
def _run_admin(self, action: str):
|
||||
cmd = ["pkexec", "/usr/sbin/saikyo-av-admin", action]
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if proc.returncode == 0:
|
||||
self.refresh()
|
||||
out = (proc.stdout or "").strip()
|
||||
if out:
|
||||
QtWidgets.QMessageBox.information(self, "Done", out)
|
||||
else:
|
||||
msg = (proc.stderr or proc.stdout or "failed").strip()
|
||||
QtWidgets.QMessageBox.warning(self, "Failed", msg)
|
||||
|
||||
def _update_scan_elapsed(self):
|
||||
if self._scan_proc is None:
|
||||
return
|
||||
ms = self._scan_timer.elapsed()
|
||||
self.scan_status.setText(f"Scanning… {int(ms/1000)}s")
|
||||
|
||||
def _stop_scan(self):
|
||||
if self._scan_proc is None:
|
||||
return
|
||||
r = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
"Stop scan",
|
||||
"Stop the running scan?",
|
||||
)
|
||||
if r != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
try:
|
||||
self._scan_proc.kill()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _run_admin_with_arg(self, action: str, arg: str):
|
||||
cmd = ["pkexec", "/usr/sbin/saikyo-av-admin", action, arg]
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if proc.returncode == 0:
|
||||
self.refresh()
|
||||
out = (proc.stdout or "").strip()
|
||||
if out:
|
||||
QtWidgets.QMessageBox.information(self, "Done", out)
|
||||
else:
|
||||
msg = (proc.stderr or proc.stdout or "failed").strip()
|
||||
QtWidgets.QMessageBox.warning(self, "Failed", msg)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(prog="saikyo-av-gui")
|
||||
ap.add_argument("--tray", action="store_true")
|
||||
ap.add_argument("--reports-dir", default=REPORTS_DIR_DEFAULT)
|
||||
args = ap.parse_args()
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
w = MainWindow(pathlib.Path(args.reports_dir), start_in_tray=args.tray)
|
||||
w.show()
|
||||
return app.exec_()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
323
saikyo-av-gui/debian/saikyo-av-gui/usr/sbin/saikyo-av-admin
Executable file
323
saikyo-av-gui/debian/saikyo-av-gui/usr/sbin/saikyo-av-admin
Executable file
|
|
@ -0,0 +1,323 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPORTS_DIR="/var/lib/saikyo-av/reports"
|
||||
LOG_DIR="/var/log/saikyo-av"
|
||||
QUAR_DIR="/var/lib/saikyo-av/quarantine"
|
||||
|
||||
mkdir -p "${REPORTS_DIR}" "${LOG_DIR}" "${QUAR_DIR}" || true
|
||||
chmod 0755 /var/lib/saikyo-av "${REPORTS_DIR}" "${LOG_DIR}" 2>/dev/null || true
|
||||
|
||||
cmd="${1:-}"
|
||||
shift || true
|
||||
|
||||
utc_ts() {
|
||||
date -u +%Y-%m-%dT%H:%M:%SZ
|
||||
}
|
||||
|
||||
write_report() {
|
||||
local id="$1"
|
||||
local summary="$2"
|
||||
local severity="$3"
|
||||
local outfile="${REPORTS_DIR}/${id}.json"
|
||||
local created
|
||||
created="$(utc_ts)"
|
||||
|
||||
# shellcheck disable=SC2129
|
||||
{
|
||||
echo "{"
|
||||
echo " \"created_utc\": \"${created}\","
|
||||
echo " \"severity\": \"${severity}\","
|
||||
echo " \"summary\": \"${summary}\","
|
||||
echo " \"details\": {"
|
||||
echo " \"action\": \"${cmd}\","
|
||||
echo " \"argv\": \"$*\""
|
||||
echo " },"
|
||||
echo " \"artifacts\": {},"
|
||||
echo " \"suggested_fixes\": []"
|
||||
echo "}"
|
||||
} > "${outfile}"
|
||||
|
||||
echo "${outfile}"
|
||||
}
|
||||
|
||||
run_to_file() {
|
||||
local out="$1"
|
||||
shift
|
||||
("$@" 2>&1 || true) | sed -e 's/\r$//' > "${out}"
|
||||
}
|
||||
|
||||
json_escape() {
|
||||
# Minimal JSON string escape (no unicode handling needed for our file paths)
|
||||
sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\t/\\t/g' -e 's/\r/\\r/g' -e 's/\n/\\n/g'
|
||||
}
|
||||
|
||||
write_scan_report() {
|
||||
local id="$1"
|
||||
local summary="$2"
|
||||
local severity="$3"
|
||||
local log_file="$4"
|
||||
local detections_tsv="$5"
|
||||
local outfile="${REPORTS_DIR}/${id}.json"
|
||||
local created
|
||||
created="$(utc_ts)"
|
||||
|
||||
local scanned_count infected_count
|
||||
scanned_count="$(grep -E '^Scanned files:' "${log_file}" 2>/dev/null | awk -F': ' '{print $2}' | tail -n 1 || true)"
|
||||
infected_count="$(grep -E '^Infected files:' "${log_file}" 2>/dev/null | awk -F': ' '{print $2}' | tail -n 1 || true)"
|
||||
scanned_count="${scanned_count:-0}"
|
||||
infected_count="${infected_count:-0}"
|
||||
|
||||
{
|
||||
echo "{"
|
||||
echo " \"created_utc\": \"${created}\","
|
||||
echo " \"severity\": \"${severity}\","
|
||||
echo " \"summary\": \"${summary}\","
|
||||
echo " \"details\": {"
|
||||
echo " \"action\": \"${cmd}\","
|
||||
echo " \"argv\": \"$*\","
|
||||
echo " \"scanned_files\": ${scanned_count},"
|
||||
echo " \"infected_files\": ${infected_count},"
|
||||
echo " \"detections\": ["
|
||||
|
||||
if [[ -s "${detections_tsv}" ]]; then
|
||||
first=1
|
||||
while IFS=$'\t' read -r pth sig; do
|
||||
[[ -n "${pth}" ]] || continue
|
||||
pth_esc="$(printf '%s' "${pth}" | json_escape)"
|
||||
sig_esc="$(printf '%s' "${sig}" | json_escape)"
|
||||
if [[ "${first}" -eq 1 ]]; then
|
||||
first=0
|
||||
else
|
||||
echo ","
|
||||
fi
|
||||
printf ' {"path":"%s","name":"%s"}' "${pth_esc}" "${sig_esc}"
|
||||
done < "${detections_tsv}"
|
||||
echo
|
||||
fi
|
||||
|
||||
echo " ]"
|
||||
echo " },"
|
||||
echo " \"artifacts\": {"
|
||||
echo " \"clamav_log\": \"${log_file}\","
|
||||
echo " \"detections_tsv\": \"${detections_tsv}\""
|
||||
echo " },"
|
||||
echo " \"suggested_fixes\": ["
|
||||
echo " {\"id\":\"quarantine\",\"title\":\"Quarantine infected file\",\"description\":\"Move selected file to local quarantine.\",\"requires_consent\":true},"
|
||||
echo " {\"id\":\"delete\",\"title\":\"Delete infected file\",\"description\":\"Delete selected infected file (dangerous).\",\"requires_consent\":true}"
|
||||
echo " ]"
|
||||
echo "}"
|
||||
} > "${outfile}"
|
||||
|
||||
echo "${outfile}"
|
||||
}
|
||||
|
||||
svc_is_enabled() {
|
||||
systemctl is-enabled saikyo-avd.timer 2>/dev/null || true
|
||||
}
|
||||
|
||||
svc_is_active() {
|
||||
systemctl is-active saikyo-avd.timer 2>/dev/null || true
|
||||
}
|
||||
|
||||
case "${cmd}" in
|
||||
health)
|
||||
echo "ok"
|
||||
;;
|
||||
|
||||
status-protection)
|
||||
echo "enabled=$(svc_is_enabled) active=$(svc_is_active)"
|
||||
;;
|
||||
|
||||
run-evidence)
|
||||
outdir="${REPORTS_DIR}/artifacts"
|
||||
mkdir -p "${outdir}" || true
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
evidence_out="${outdir}/saikyo-evidence-${ts}.log"
|
||||
|
||||
if command -v saikyo-evidence >/dev/null 2>&1; then
|
||||
run_to_file "${evidence_out}" saikyo-evidence
|
||||
elif [ -x /usr/bin/saikyo-evidence ]; then
|
||||
run_to_file "${evidence_out}" /usr/bin/saikyo-evidence
|
||||
else
|
||||
echo "saikyo-evidence not found" > "${evidence_out}"
|
||||
fi
|
||||
|
||||
rpt="$(write_report "evidence-${ts}" "Evidence report generated" "info" "${cmd}")"
|
||||
# Append artifacts into report JSON (minimal, without jq dependency).
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"evidence_log\": \"${evidence_out}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
scan)
|
||||
outdir="${REPORTS_DIR}/artifacts"
|
||||
mkdir -p "${outdir}" || true
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
log_file="${outdir}/clamav-scan-${ts}.log"
|
||||
det_tsv="${outdir}/clamav-detections-${ts}.tsv"
|
||||
|
||||
if ! command -v clamscan >/dev/null 2>&1; then
|
||||
echo "clamscan not found (install clamav)" > "${log_file}"
|
||||
rpt="$(write_report "scan-${ts}" "ClamAV scan failed: clamscan not found" "warn" "${cmd}")"
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"clamav_log\": \"${log_file}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Full scan of / with exclusions.
|
||||
# - exclude hidden paths (/.*/)
|
||||
# - exclude virtual/system dirs
|
||||
# - keep a log we can parse
|
||||
(
|
||||
clamscan \
|
||||
-r \
|
||||
--infected \
|
||||
--no-summary \
|
||||
--exclude-dir='^/proc/' \
|
||||
--exclude-dir='^/sys/' \
|
||||
--exclude-dir='^/dev/' \
|
||||
--exclude-dir='^/run/' \
|
||||
--exclude-dir='^/tmp/' \
|
||||
--exclude-dir='/.*/' \
|
||||
/ \
|
||||
2>&1
|
||||
echo
|
||||
clamscan -r --infected --exclude-dir='^/proc/' --exclude-dir='^/sys/' --exclude-dir='^/dev/' --exclude-dir='^/run/' --exclude-dir='^/tmp/' --exclude-dir='/.*/' / --summary 2>/dev/null || true
|
||||
) | sed -e 's/\r$//' > "${log_file}"
|
||||
|
||||
: > "${det_tsv}"
|
||||
# Parse "PATH: SIGNATURE FOUND" lines.
|
||||
grep -E ' FOUND$' "${log_file}" | sed -E 's/: (.+) FOUND$//;t;d' >/dev/null 2>&1 || true
|
||||
while IFS= read -r line; do
|
||||
# Example: /path/file: Eicar-Test-Signature FOUND
|
||||
pth="${line%%:*}"
|
||||
rest="${line#*: }"
|
||||
sig="${rest% FOUND}"
|
||||
printf '%s\t%s\n' "${pth}" "${sig}" >> "${det_tsv}"
|
||||
done < <(grep -E ' FOUND$' "${log_file}" || true)
|
||||
|
||||
infected_count="$(wc -l < "${det_tsv}" 2>/dev/null || echo 0)"
|
||||
severity="info"
|
||||
summary="ClamAV scan completed: no threats"
|
||||
if [[ "${infected_count}" -gt 0 ]]; then
|
||||
severity="bad"
|
||||
summary="ClamAV scan completed: ${infected_count} threat(s) found"
|
||||
fi
|
||||
|
||||
rpt="$(write_scan_report "scan-${ts}" "${summary}" "${severity}" "${log_file}" "${det_tsv}" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
quarantine)
|
||||
target="${1:-}"
|
||||
if [[ -z "${target}" ]]; then
|
||||
echo "missing file path" >&2
|
||||
exit 2
|
||||
fi
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
bn="$(basename -- "${target}" 2>/dev/null || echo file)"
|
||||
dest="${QUAR_DIR}/${ts}-${bn}"
|
||||
if [[ ! -f "${target}" ]]; then
|
||||
rpt="$(write_report "quarantine-${ts}" "Quarantine failed: file not found" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
fi
|
||||
mv -f -- "${target}" "${dest}" 2>>"${LOG_DIR}/saikyo-av-admin.log" || {
|
||||
rpt="$(write_report "quarantine-${ts}" "Quarantine failed: move error" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
}
|
||||
rpt="$(write_report "quarantine-${ts}" "File quarantined" "warn" "${cmd}")"
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"from\": \"${target}\", \"to\": \"${dest}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
delete)
|
||||
target="${1:-}"
|
||||
if [[ -z "${target}" ]]; then
|
||||
echo "missing file path" >&2
|
||||
exit 2
|
||||
fi
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
if [[ ! -e "${target}" ]]; then
|
||||
rpt="$(write_report "delete-${ts}" "Delete failed: path not found" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
fi
|
||||
rm -rf -- "${target}" 2>>"${LOG_DIR}/saikyo-av-admin.log" || {
|
||||
rpt="$(write_report "delete-${ts}" "Delete failed" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
}
|
||||
rpt="$(write_report "delete-${ts}" "File deleted" "warn" "${cmd}")"
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"path\": \"${target}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
run-audit)
|
||||
outdir="${REPORTS_DIR}/artifacts"
|
||||
mkdir -p "${outdir}" || true
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
audit_out="${outdir}/saikyo-audit-report-${ts}.txt"
|
||||
|
||||
if command -v saikyo-audit-report >/dev/null 2>&1; then
|
||||
run_to_file "${audit_out}" saikyo-audit-report
|
||||
else
|
||||
echo "saikyo-audit-report not installed" > "${audit_out}"
|
||||
fi
|
||||
|
||||
rpt="$(write_report "audit-${ts}" "Audit report executed" "info" "${cmd}")"
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"audit_stdout\": \"${audit_out}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
collect-artifacts)
|
||||
outdir="${REPORTS_DIR}/artifacts"
|
||||
mkdir -p "${outdir}" || true
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
collect_out="${outdir}/collect-artifacts-${ts}.log"
|
||||
|
||||
if [ -x /usr/share/saikyo-os/forensics/collect-artifacts.sh ]; then
|
||||
run_to_file "${collect_out}" /usr/share/saikyo-os/forensics/collect-artifacts.sh
|
||||
else
|
||||
echo "collect-artifacts.sh not found" > "${collect_out}"
|
||||
fi
|
||||
|
||||
rpt="$(write_report "collect-${ts}" "Artifacts collection executed" "info" "${cmd}")"
|
||||
sed -i "s#\"artifacts\": {}#\"artifacts\": {\"collector_log\": \"${collect_out}\"}#" "${rpt}" || true
|
||||
echo "${rpt}"
|
||||
;;
|
||||
|
||||
enable-protection)
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
(systemctl daemon-reload 2>&1 || true) >> "${LOG_DIR}/saikyo-av-admin.log" || true
|
||||
if systemctl enable --now saikyo-avd.timer >/dev/null 2>&1; then
|
||||
rpt="$(write_report "enable-${ts}" "Protection enabled" "info" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 0
|
||||
else
|
||||
rpt="$(write_report "enable-${ts}" "Protection enable failed" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
disable-protection)
|
||||
ts="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
(systemctl daemon-reload 2>&1 || true) >> "${LOG_DIR}/saikyo-av-admin.log" || true
|
||||
if systemctl disable --now saikyo-avd.timer >/dev/null 2>&1; then
|
||||
rpt="$(write_report "disable-${ts}" "Protection disabled" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 0
|
||||
else
|
||||
rpt="$(write_report "disable-${ts}" "Protection disable failed" "warn" "${cmd}")"
|
||||
echo "${rpt}"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: saikyo-av-admin {health|status-protection|run-evidence|run-audit|collect-artifacts|scan|quarantine <file>|delete <file>|enable-protection|disable-protection}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Saikyo Antivirus
|
||||
Comment=Saikyo Antivirus admin panel
|
||||
Exec=/usr/bin/saikyo-av-gui
|
||||
Icon=saikyo-av
|
||||
Terminal=false
|
||||
Categories=System;Security;
|
||||
Binary file not shown.
|
|
@ -0,0 +1,26 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: saikyo-av-gui
|
||||
Source: https://saikyo-os.ru/
|
||||
|
||||
Files: *
|
||||
Copyright: 2026 SAIKYO OS
|
||||
License: MIT
|
||||
|
||||
License: MIT
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" x2="1" y1="0" y2="1">
|
||||
<stop offset="0" stop-color="#60a5fa"/>
|
||||
<stop offset="1" stop-color="#2dd4bf"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="24" y="24" width="208" height="208" rx="48" fill="#0b1220"/>
|
||||
<path d="M128 40c40 20 72 16 88 24v72c0 52-36 88-88 104C76 224 40 188 40 136V64c16-8 48-4 88-24z" fill="url(#g)" opacity="0.95"/>
|
||||
<path d="M94 132l22 22 50-58" fill="none" stroke="#0b1220" stroke-width="16" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 616 B |
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
|
||||
<policyconfig>
|
||||
<action id="org.saikyo.av.admin">
|
||||
<description>Run Saikyo Antivirus administrative actions</description>
|
||||
<message>Authentication is required to run Saikyo Antivirus administrative actions.</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>auth_admin</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">/usr/sbin/saikyo-av-admin</annotate>
|
||||
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
|
||||
</action>
|
||||
</policyconfig>
|
||||
1
saikyo-av-gui/debian/source/format
Normal file
1
saikyo-av-gui/debian/source/format
Normal file
|
|
@ -0,0 +1 @@
|
|||
3.0 (native)
|
||||
8
saikyo-av-gui/desktop/saikyo-av-gui.desktop
Normal file
8
saikyo-av-gui/desktop/saikyo-av-gui.desktop
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Saikyo Antivirus
|
||||
Comment=Saikyo Antivirus admin panel
|
||||
Exec=/usr/bin/saikyo-av-gui
|
||||
Icon=saikyo-av
|
||||
Terminal=false
|
||||
Categories=System;Security;
|
||||
11
saikyo-av-gui/icons/saikyo-av.svg
Normal file
11
saikyo-av-gui/icons/saikyo-av.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" x2="1" y1="0" y2="1">
|
||||
<stop offset="0" stop-color="#60a5fa"/>
|
||||
<stop offset="1" stop-color="#2dd4bf"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="24" y="24" width="208" height="208" rx="48" fill="#0b1220"/>
|
||||
<path d="M128 40c40 20 72 16 88 24v72c0 52-36 88-88 104C76 224 40 188 40 136V64c16-8 48-4 88-24z" fill="url(#g)" opacity="0.95"/>
|
||||
<path d="M94 132l22 22 50-58" fill="none" stroke="#0b1220" stroke-width="16" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 616 B |
15
saikyo-av-gui/polkit/org.saikyo.av.admin.policy
Normal file
15
saikyo-av-gui/polkit/org.saikyo.av.admin.policy
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
|
||||
<policyconfig>
|
||||
<action id="org.saikyo.av.admin">
|
||||
<description>Run Saikyo Antivirus administrative actions</description>
|
||||
<message>Authentication is required to run Saikyo Antivirus administrative actions.</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>auth_admin</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">/usr/sbin/saikyo-av-admin</annotate>
|
||||
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
|
||||
</action>
|
||||
</policyconfig>
|
||||
52
saikyo-av/bin/saikyo-avd
Normal file
52
saikyo-av/bin/saikyo-avd
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPORTS_DIR="/var/lib/saikyo-av/reports"
|
||||
ART_DIR="${REPORTS_DIR}/artifacts"
|
||||
|
||||
mkdir -p "${REPORTS_DIR}" "${ART_DIR}" || true
|
||||
chmod 0755 /var/lib/saikyo-av "${REPORTS_DIR}" 2>/dev/null || true
|
||||
|
||||
TS="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
REPORT_ID="agent-${TS}"
|
||||
OUT_JSON="${REPORTS_DIR}/${REPORT_ID}.json"
|
||||
|
||||
FAILED_UNITS_FILE="${ART_DIR}/systemctl-failed-${TS}.txt"
|
||||
JOURNAL_FILE="${ART_DIR}/journal-warn-${TS}.txt"
|
||||
|
||||
(systemctl --failed 2>&1 || true) > "${FAILED_UNITS_FILE}"
|
||||
(journalctl -b -p warning..alert --no-pager 2>&1 | tail -n 400 || true) > "${JOURNAL_FILE}"
|
||||
|
||||
FAILED_COUNT="$(grep -cE '^[^\s].*\.service' "${FAILED_UNITS_FILE}" 2>/dev/null || echo 0)"
|
||||
|
||||
SEVERITY="info"
|
||||
SUMMARY="Saikyo AV: periodic health report"
|
||||
if [[ "${FAILED_COUNT}" -gt 0 ]]; then
|
||||
SEVERITY="warn"
|
||||
SUMMARY="Saikyo AV: detected failed systemd units (${FAILED_COUNT})"
|
||||
fi
|
||||
|
||||
cat > "${OUT_JSON}" <<EOF
|
||||
{
|
||||
"created_utc": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"severity": "${SEVERITY}",
|
||||
"summary": "${SUMMARY}",
|
||||
"details": {
|
||||
"failed_units_count": ${FAILED_COUNT}
|
||||
},
|
||||
"artifacts": {
|
||||
"systemctl_failed": "${FAILED_UNITS_FILE}",
|
||||
"journal_warn": "${JOURNAL_FILE}"
|
||||
},
|
||||
"suggested_fixes": [
|
||||
{
|
||||
"id": "run_evidence",
|
||||
"title": "Run evidence collection",
|
||||
"description": "Generate /var/log/saikyo-evidence/latest.log using saikyo-evidence.",
|
||||
"requires_consent": true
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
saikyo-av (0.1.1) stable; urgency=medium
|
||||
|
||||
* Fix packaging: install saikyo-avd and systemd units to correct paths; cleanup broken dirs on upgrade.
|
||||
|
||||
-- SAIKYO OS <support@saikyo-os.ru> Mon, 20 Jan 2026 16:55:00 +0000
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
./bin/saikyo-avd
|
||||
./systemd/saikyo-avd.service
|
||||
./systemd/saikyo-avd.timer
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# Automatically added by dh_installsystemd/13.24.2
|
||||
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
|
||||
# The following line should be removed in trixie or trixie+1
|
||||
deb-systemd-helper unmask 'saikyo-avd.timer' >/dev/null || true
|
||||
|
||||
# was-enabled defaults to true, so new installations run enable.
|
||||
if deb-systemd-helper --quiet was-enabled 'saikyo-avd.timer'; then
|
||||
# Enables the unit on first installation, creates new
|
||||
# symlinks on upgrades if the unit file has changed.
|
||||
deb-systemd-helper enable 'saikyo-avd.timer' >/dev/null || true
|
||||
else
|
||||
# Update the statefile to add new symlinks (if any), which need to be
|
||||
# cleaned up on purge. Also remove old symlinks.
|
||||
deb-systemd-helper update-state 'saikyo-avd.timer' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
# End automatically added section
|
||||
# Automatically added by dh_installsystemd/13.24.2
|
||||
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl --system daemon-reload >/dev/null || true
|
||||
if [ -n "$2" ]; then
|
||||
_dh_action=restart
|
||||
else
|
||||
_dh_action=start
|
||||
fi
|
||||
deb-systemd-invoke $_dh_action 'saikyo-avd.service' 'saikyo-avd.timer' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
# End automatically added section
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Automatically added by dh_installsystemd/13.24.2
|
||||
if [ -z "$DPKG_ROOT" ] && [ "$1" = remove ] && [ -d /run/systemd/system ] ; then
|
||||
deb-systemd-invoke stop 'saikyo-avd.service' 'saikyo-avd.timer' >/dev/null || true
|
||||
fi
|
||||
# End automatically added section
|
||||
5
saikyo-av/debian/changelog
Normal file
5
saikyo-av/debian/changelog
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
saikyo-av (0.1.1) stable; urgency=medium
|
||||
|
||||
* Fix packaging: install saikyo-avd and systemd units to correct paths; cleanup broken dirs on upgrade.
|
||||
|
||||
-- SAIKYO OS <support@saikyo-os.ru> Mon, 20 Jan 2026 16:55:00 +0000
|
||||
13
saikyo-av/debian/control
Normal file
13
saikyo-av/debian/control
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
Source: saikyo-av
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Maintainer: SAIKYO OS <support@saikyo-os.ru>
|
||||
Build-Depends: debhelper-compat (= 13)
|
||||
Standards-Version: 4.6.2
|
||||
Rules-Requires-Root: no
|
||||
|
||||
Package: saikyo-av
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, bash, coreutils, systemd | systemd-sysv, util-linux, grep, sed, gawk
|
||||
Description: Saikyo Antivirus agent (local)
|
||||
Local always-on agent for collecting security signals and producing local reports.
|
||||
26
saikyo-av/debian/copyright
Normal file
26
saikyo-av/debian/copyright
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: saikyo-av
|
||||
Source: https://saikyo-os.ru/
|
||||
|
||||
Files: *
|
||||
Copyright: 2026 SAIKYO OS
|
||||
License: MIT
|
||||
|
||||
License: MIT
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1
saikyo-av/debian/debhelper-build-stamp
Normal file
1
saikyo-av/debian/debhelper-build-stamp
Normal file
|
|
@ -0,0 +1 @@
|
|||
saikyo-av
|
||||
2
saikyo-av/debian/files
Normal file
2
saikyo-av/debian/files
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
saikyo-av_0.1.1_all.deb admin optional
|
||||
saikyo-av_0.1.1_amd64.buildinfo admin optional
|
||||
3
saikyo-av/debian/install
Normal file
3
saikyo-av/debian/install
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
bin/saikyo-avd usr/sbin/
|
||||
systemd/saikyo-avd.service lib/systemd/system/
|
||||
systemd/saikyo-avd.timer lib/systemd/system/
|
||||
15
saikyo-av/debian/rules
Executable file
15
saikyo-av/debian/rules
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_build:
|
||||
|
||||
override_dh_auto_test:
|
||||
|
||||
override_dh_fixperms:
|
||||
dh_fixperms
|
||||
chmod 0755 debian/saikyo-av/usr/sbin/saikyo-avd
|
||||
chmod 0755 debian/saikyo-av.preinst
|
||||
chmod 0755 debian/saikyo-av.postinst
|
||||
chmod 0755 debian/saikyo-av.prerm
|
||||
1
saikyo-av/debian/saikyo-av.debhelper.log
Normal file
1
saikyo-av/debian/saikyo-av.debhelper.log
Normal file
|
|
@ -0,0 +1 @@
|
|||
dh_fixperms
|
||||
13
saikyo-av/debian/saikyo-av.postinst
Executable file
13
saikyo-av/debian/saikyo-av.postinst
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
mkdir -p /var/lib/saikyo-av/reports /var/lib/saikyo-av/reports/artifacts || true
|
||||
chmod 0755 /var/lib/saikyo-av /var/lib/saikyo-av/reports 2>/dev/null || true
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
systemctl enable --now saikyo-avd.timer >/dev/null 2>&1 || true
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
12
saikyo-av/debian/saikyo-av.postrm.debhelper
Normal file
12
saikyo-av/debian/saikyo-av.postrm.debhelper
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Automatically added by dh_installsystemd/13.24.2
|
||||
if [ "$1" = remove ] && [ -d /run/systemd/system ] ; then
|
||||
systemctl --system daemon-reload >/dev/null || true
|
||||
fi
|
||||
# End automatically added section
|
||||
# Automatically added by dh_installsystemd/13.24.2
|
||||
if [ "$1" = "purge" ]; then
|
||||
if [ -x "/usr/bin/deb-systemd-helper" ]; then
|
||||
deb-systemd-helper purge 'saikyo-avd.timer' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
# End automatically added section
|
||||
22
saikyo-av/debian/saikyo-av.preinst
Executable file
22
saikyo-av/debian/saikyo-av.preinst
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Fixup for 0.1.0 packaging mistake where dh_install created directories:
|
||||
# /usr/sbin/saikyo-avd/saikyo-avd
|
||||
# /lib/systemd/system/saikyo-avd.service/saikyo-avd.service
|
||||
# /lib/systemd/system/saikyo-avd.timer/saikyo-avd.timer
|
||||
# Remove them before unpacking the corrected package.
|
||||
|
||||
if [[ "$1" == "upgrade" || "$1" == "install" ]]; then
|
||||
for d in \
|
||||
/usr/sbin/saikyo-avd \
|
||||
/lib/systemd/system/saikyo-avd.service \
|
||||
/lib/systemd/system/saikyo-avd.timer
|
||||
do
|
||||
if [[ -d "$d" ]]; then
|
||||
rm -rf "$d" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
exit 0
|
||||
10
saikyo-av/debian/saikyo-av.prerm
Executable file
10
saikyo-av/debian/saikyo-av.prerm
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
case "$1" in
|
||||
remove|deconfigure)
|
||||
systemctl disable --now saikyo-avd.timer >/dev/null 2>&1 || true
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
2
saikyo-av/debian/saikyo-av.substvars
Normal file
2
saikyo-av/debian/saikyo-av.substvars
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
misc:Depends=
|
||||
misc:Pre-Depends=
|
||||
10
saikyo-av/debian/saikyo-av/DEBIAN/control
Normal file
10
saikyo-av/debian/saikyo-av/DEBIAN/control
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
Package: saikyo-av
|
||||
Version: 0.1.1
|
||||
Architecture: all
|
||||
Maintainer: SAIKYO OS <support@saikyo-os.ru>
|
||||
Installed-Size: 21
|
||||
Depends: bash, coreutils, systemd | systemd-sysv, util-linux, grep, sed, gawk
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Description: Saikyo Antivirus agent (local)
|
||||
Local always-on agent for collecting security signals and producing local reports.
|
||||
5
saikyo-av/debian/saikyo-av/DEBIAN/md5sums
Normal file
5
saikyo-av/debian/saikyo-av/DEBIAN/md5sums
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
170cbb33110db84791e38ef0d265367b lib/systemd/system/saikyo-avd.service
|
||||
706f24a7d93f1588f6130236a0cc30dc lib/systemd/system/saikyo-avd.timer
|
||||
428c2feb89dbdabeb07df9c07a55931e usr/sbin/saikyo-avd
|
||||
1d3486cb4986868f799ac11f32b7c69f usr/share/doc/saikyo-av/changelog.gz
|
||||
ba541e7320b5e3963eae2a7ad3e12502 usr/share/doc/saikyo-av/copyright
|
||||
13
saikyo-av/debian/saikyo-av/DEBIAN/postinst
Executable file
13
saikyo-av/debian/saikyo-av/DEBIAN/postinst
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
mkdir -p /var/lib/saikyo-av/reports /var/lib/saikyo-av/reports/artifacts || true
|
||||
chmod 0755 /var/lib/saikyo-av /var/lib/saikyo-av/reports 2>/dev/null || true
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
systemctl enable --now saikyo-avd.timer >/dev/null 2>&1 || true
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
14
saikyo-av/debian/saikyo-av/DEBIAN/postrm
Executable file
14
saikyo-av/debian/saikyo-av/DEBIAN/postrm
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
# Automatically added by dh_installsystemd/13.24.2
|
||||
if [ "$1" = remove ] && [ -d /run/systemd/system ] ; then
|
||||
systemctl --system daemon-reload >/dev/null || true
|
||||
fi
|
||||
# End automatically added section
|
||||
# Automatically added by dh_installsystemd/13.24.2
|
||||
if [ "$1" = "purge" ]; then
|
||||
if [ -x "/usr/bin/deb-systemd-helper" ]; then
|
||||
deb-systemd-helper purge 'saikyo-avd.timer' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
# End automatically added section
|
||||
22
saikyo-av/debian/saikyo-av/DEBIAN/preinst
Executable file
22
saikyo-av/debian/saikyo-av/DEBIAN/preinst
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Fixup for 0.1.0 packaging mistake where dh_install created directories:
|
||||
# /usr/sbin/saikyo-avd/saikyo-avd
|
||||
# /lib/systemd/system/saikyo-avd.service/saikyo-avd.service
|
||||
# /lib/systemd/system/saikyo-avd.timer/saikyo-avd.timer
|
||||
# Remove them before unpacking the corrected package.
|
||||
|
||||
if [[ "$1" == "upgrade" || "$1" == "install" ]]; then
|
||||
for d in \
|
||||
/usr/sbin/saikyo-avd \
|
||||
/lib/systemd/system/saikyo-avd.service \
|
||||
/lib/systemd/system/saikyo-avd.timer
|
||||
do
|
||||
if [[ -d "$d" ]]; then
|
||||
rm -rf "$d" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
exit 0
|
||||
10
saikyo-av/debian/saikyo-av/DEBIAN/prerm
Executable file
10
saikyo-av/debian/saikyo-av/DEBIAN/prerm
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
case "$1" in
|
||||
remove|deconfigure)
|
||||
systemctl disable --now saikyo-avd.timer >/dev/null 2>&1 || true
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
[Unit]
|
||||
Description=Saikyo Antivirus Agent (report generator)
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/sbin/saikyo-avd
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service
|
||||
ReadWritePaths=/var/lib/saikyo-av/reports
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
[Unit]
|
||||
Description=Saikyo Antivirus Agent timer
|
||||
|
||||
[Timer]
|
||||
OnBootSec=2min
|
||||
OnUnitActiveSec=5min
|
||||
AccuracySec=30s
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
52
saikyo-av/debian/saikyo-av/usr/sbin/saikyo-avd
Executable file
52
saikyo-av/debian/saikyo-av/usr/sbin/saikyo-avd
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPORTS_DIR="/var/lib/saikyo-av/reports"
|
||||
ART_DIR="${REPORTS_DIR}/artifacts"
|
||||
|
||||
mkdir -p "${REPORTS_DIR}" "${ART_DIR}" || true
|
||||
chmod 0755 /var/lib/saikyo-av "${REPORTS_DIR}" 2>/dev/null || true
|
||||
|
||||
TS="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
REPORT_ID="agent-${TS}"
|
||||
OUT_JSON="${REPORTS_DIR}/${REPORT_ID}.json"
|
||||
|
||||
FAILED_UNITS_FILE="${ART_DIR}/systemctl-failed-${TS}.txt"
|
||||
JOURNAL_FILE="${ART_DIR}/journal-warn-${TS}.txt"
|
||||
|
||||
(systemctl --failed 2>&1 || true) > "${FAILED_UNITS_FILE}"
|
||||
(journalctl -b -p warning..alert --no-pager 2>&1 | tail -n 400 || true) > "${JOURNAL_FILE}"
|
||||
|
||||
FAILED_COUNT="$(grep -cE '^[^\s].*\.service' "${FAILED_UNITS_FILE}" 2>/dev/null || echo 0)"
|
||||
|
||||
SEVERITY="info"
|
||||
SUMMARY="Saikyo AV: periodic health report"
|
||||
if [[ "${FAILED_COUNT}" -gt 0 ]]; then
|
||||
SEVERITY="warn"
|
||||
SUMMARY="Saikyo AV: detected failed systemd units (${FAILED_COUNT})"
|
||||
fi
|
||||
|
||||
cat > "${OUT_JSON}" <<EOF
|
||||
{
|
||||
"created_utc": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"severity": "${SEVERITY}",
|
||||
"summary": "${SUMMARY}",
|
||||
"details": {
|
||||
"failed_units_count": ${FAILED_COUNT}
|
||||
},
|
||||
"artifacts": {
|
||||
"systemctl_failed": "${FAILED_UNITS_FILE}",
|
||||
"journal_warn": "${JOURNAL_FILE}"
|
||||
},
|
||||
"suggested_fixes": [
|
||||
{
|
||||
"id": "run_evidence",
|
||||
"title": "Run evidence collection",
|
||||
"description": "Generate /var/log/saikyo-evidence/latest.log using saikyo-evidence.",
|
||||
"requires_consent": true
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
BIN
saikyo-av/debian/saikyo-av/usr/share/doc/saikyo-av/changelog.gz
Normal file
BIN
saikyo-av/debian/saikyo-av/usr/share/doc/saikyo-av/changelog.gz
Normal file
Binary file not shown.
26
saikyo-av/debian/saikyo-av/usr/share/doc/saikyo-av/copyright
Normal file
26
saikyo-av/debian/saikyo-av/usr/share/doc/saikyo-av/copyright
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: saikyo-av
|
||||
Source: https://saikyo-os.ru/
|
||||
|
||||
Files: *
|
||||
Copyright: 2026 SAIKYO OS
|
||||
License: MIT
|
||||
|
||||
License: MIT
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1
saikyo-av/debian/source/format
Normal file
1
saikyo-av/debian/source/format
Normal file
|
|
@ -0,0 +1 @@
|
|||
3.0 (native)
|
||||
19
saikyo-av/systemd/saikyo-avd.service
Normal file
19
saikyo-av/systemd/saikyo-avd.service
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[Unit]
|
||||
Description=Saikyo Antivirus Agent (report generator)
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/sbin/saikyo-avd
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service
|
||||
ReadWritePaths=/var/lib/saikyo-av/reports
|
||||
11
saikyo-av/systemd/saikyo-avd.timer
Normal file
11
saikyo-av/systemd/saikyo-avd.timer
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[Unit]
|
||||
Description=Saikyo Antivirus Agent timer
|
||||
|
||||
[Timer]
|
||||
OnBootSec=2min
|
||||
OnUnitActiveSec=5min
|
||||
AccuracySec=30s
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=SAIKYO OS First Login (KDE)
|
||||
Exec=/usr/bin/saikyo-first-login-kde
|
||||
Terminal=false
|
||||
OnlyShowIn=KDE;
|
||||
X-KDE-autostart-phase=2
|
||||
102
saikyo-branding/assets/bin/saikyo-evidence
Normal file
102
saikyo-branding/assets/bin/saikyo-evidence
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
OUT_DIR="/var/log/saikyo-evidence"
|
||||
TS="$(date -u +%Y%m%dT%H%M%SZ 2>/dev/null || date +%s)"
|
||||
OUT_FILE="${OUT_DIR}/saikyo-evidence-${TS}.log"
|
||||
|
||||
mkdir -p "${OUT_DIR}" 2>/dev/null || true
|
||||
chmod 0755 "${OUT_DIR}" 2>/dev/null || true
|
||||
|
||||
dumpln() {
|
||||
printf '\n==== %s ====\n' "$1"
|
||||
}
|
||||
|
||||
{
|
||||
echo "saikyo_evidence_version=1"
|
||||
echo "timestamp_utc=${TS}"
|
||||
|
||||
dumpln "os-release"
|
||||
cat /etc/os-release 2>/dev/null || true
|
||||
|
||||
dumpln "lsb-release"
|
||||
cat /etc/lsb-release 2>/dev/null || true
|
||||
|
||||
dumpln "uname"
|
||||
uname -a 2>/dev/null || true
|
||||
|
||||
dumpln "dpkg saikyo packages"
|
||||
dpkg -l 2>/dev/null | grep -i saikyo || true
|
||||
|
||||
dumpln "apt sources"
|
||||
if [ -f /etc/apt/sources.list ]; then
|
||||
echo "--- /etc/apt/sources.list ---"
|
||||
sed -n '1,200p' /etc/apt/sources.list 2>/dev/null || true
|
||||
fi
|
||||
if [ -d /etc/apt/sources.list.d ]; then
|
||||
echo "--- /etc/apt/sources.list.d ---"
|
||||
ls -la /etc/apt/sources.list.d 2>/dev/null || true
|
||||
for f in /etc/apt/sources.list.d/*; do
|
||||
[ -e "$f" ] || continue
|
||||
echo "--- $f ---"
|
||||
sed -n '1,200p' "$f" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
|
||||
dumpln "apt sources forbidden patterns"
|
||||
grep -RIn --line-number -E 'deb\.debian\.org|security\.debian\.org|cdrom:|archive\.ubuntu\.com' /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null || true
|
||||
|
||||
dumpln "apt periodic config"
|
||||
if [ -f /etc/apt/apt.conf.d/20saikyo-periodic ]; then
|
||||
echo "20saikyo-periodic=present"
|
||||
cat /etc/apt/apt.conf.d/20saikyo-periodic 2>/dev/null || true
|
||||
else
|
||||
echo "20saikyo-periodic=MISSING"
|
||||
fi
|
||||
|
||||
dumpln "auto update units state"
|
||||
for u in apt-daily.timer apt-daily-upgrade.timer unattended-upgrades.service packagekit-offline-update.timer packagekit-offline-update.service; do
|
||||
echo "unit=${u} enabled=$(systemctl is-enabled "$u" 2>/dev/null || echo unknown) active=$(systemctl is-active "$u" 2>/dev/null || echo unknown) masked=$(systemctl is-masked "$u" 2>/dev/null || echo unknown)"
|
||||
done
|
||||
|
||||
dumpln "packagekit state"
|
||||
echo "packagekit enabled=$(systemctl is-enabled packagekit.service 2>/dev/null || echo unknown) active=$(systemctl is-active packagekit.service 2>/dev/null || echo unknown) masked=$(systemctl is-masked packagekit.service 2>/dev/null || echo unknown)"
|
||||
|
||||
dumpln "system timers (filtered)"
|
||||
systemctl list-timers --all 2>/dev/null | grep -E 'apt|unattended|packagekit|flatpak|snap' || true
|
||||
|
||||
dumpln "license"
|
||||
if command -v saikyo-license >/dev/null 2>&1; then
|
||||
echo "saikyo-license=present"
|
||||
if /usr/sbin/saikyo-license verify >/dev/null 2>&1; then
|
||||
echo "license_verify=ok"
|
||||
else
|
||||
echo "license_verify=fail"
|
||||
fi
|
||||
else
|
||||
echo "saikyo-license=missing"
|
||||
fi
|
||||
|
||||
dumpln "secure boot"
|
||||
if command -v mokutil >/dev/null 2>&1; then
|
||||
mokutil --sb-state 2>&1 || true
|
||||
mokutil --list-enrolled 2>/dev/null || true
|
||||
else
|
||||
echo "mokutil=missing"
|
||||
fi
|
||||
|
||||
dumpln "branding files"
|
||||
ls -la /usr/share/saikyo-os 2>/dev/null || true
|
||||
ls -la /usr/share/wallpapers/Saikyo/contents/images 2>/dev/null || true
|
||||
ls -la /usr/share/sddm/themes/Saikyo 2>/dev/null || true
|
||||
|
||||
dumpln "visible Debian/Plasma strings (best effort)"
|
||||
grep -RIn --line-number -E '\bDebian\b|\bPlasma\b' /usr/share/applications /usr/share/metainfo 2>/dev/null | head -n 50 || true
|
||||
|
||||
} >"${OUT_FILE}" 2>/dev/null
|
||||
|
||||
chmod 0644 "${OUT_FILE}" 2>/dev/null || true
|
||||
ln -sfn "$(basename "${OUT_FILE}")" "${OUT_DIR}/latest.log" 2>/dev/null || true
|
||||
|
||||
echo "Wrote ${OUT_FILE}"
|
||||
exit 0
|
||||
185
saikyo-branding/assets/bin/saikyo-first-login-kde
Normal file
185
saikyo-branding/assets/bin/saikyo-first-login-kde
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
MARK_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/saikyo-os"
|
||||
MARK_FILE="${MARK_DIR}/first-login-kde.done"
|
||||
WALLPAPER_PATH="/usr/share/wallpapers/Saikyo/contents/images/saikyo-default.svg"
|
||||
LOGO_PATH="/usr/share/saikyo-os/logos/saikyo-logo.svg"
|
||||
AVATAR_PNG="/usr/share/icons/hicolor/512x512/apps/distributor-logo.png"
|
||||
COLOR_SCHEME="SaikyoDark"
|
||||
|
||||
mkdir -p "${MARK_DIR}" 2>/dev/null || true
|
||||
|
||||
if [[ -f "${MARK_FILE}" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ! -r "${WALLPAPER_PATH}" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
have_cmd() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
apply_look_and_feel() {
|
||||
if have_cmd lookandfeeltool; then
|
||||
lookandfeeltool -a org.saikyo.desktop >/dev/null 2>&1 || true
|
||||
elif have_cmd lookandfeeltool5; then
|
||||
lookandfeeltool5 -a org.saikyo.desktop >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
apply_color_scheme() {
|
||||
if have_cmd plasma-apply-colorscheme; then
|
||||
plasma-apply-colorscheme "${COLOR_SCHEME}" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
if have_cmd kwriteconfig5; then
|
||||
kwriteconfig5 --file kdeglobals --group General --key ColorScheme "${COLOR_SCHEME}" >/dev/null 2>&1 || true
|
||||
elif have_cmd kwriteconfig6; then
|
||||
kwriteconfig6 --file kdeglobals --group General --key ColorScheme "${COLOR_SCHEME}" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
restart_plasma_shell() {
|
||||
if have_cmd kquitapp5 && have_cmd kstart5; then
|
||||
kquitapp5 plasmashell >/dev/null 2>&1 || true
|
||||
nohup kstart5 plasmashell >/dev/null 2>&1 &
|
||||
elif have_cmd kquitapp6 && have_cmd kstart6; then
|
||||
kquitapp6 plasmashell >/dev/null 2>&1 || true
|
||||
nohup kstart6 plasmashell >/dev/null 2>&1 &
|
||||
fi
|
||||
}
|
||||
|
||||
set_wallpaper() {
|
||||
local qdbus_cmd="$1"
|
||||
"${qdbus_cmd}" org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript "
|
||||
var Desktops = desktops();
|
||||
for (i = 0; i < Desktops.length; i++) {
|
||||
d = Desktops[i];
|
||||
d.wallpaperPlugin = 'org.kde.image';
|
||||
d.currentConfigGroup = ['Wallpaper', 'org.kde.image', 'General'];
|
||||
d.writeConfig('Image', 'file://${WALLPAPER_PATH}');
|
||||
}
|
||||
" >/dev/null 2>&1 || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
if have_cmd qdbus; then
|
||||
set_wallpaper qdbus || true
|
||||
elif have_cmd qdbus6; then
|
||||
set_wallpaper qdbus6 || true
|
||||
fi
|
||||
|
||||
apply_color_scheme
|
||||
apply_look_and_feel
|
||||
restart_plasma_shell || true
|
||||
|
||||
add_panel_widgets_and_pins() {
|
||||
local qdbus_cmd="$1"
|
||||
"${qdbus_cmd}" org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript "
|
||||
function firstPanel() {
|
||||
var ps = panels();
|
||||
if (!ps || ps.length === 0) return null;
|
||||
return ps[0];
|
||||
}
|
||||
|
||||
function ensureWidget(panel, pluginId) {
|
||||
try {
|
||||
var ws = panel.widgets();
|
||||
for (var i = 0; i < ws.length; i++) {
|
||||
if (ws[i].type === pluginId) return;
|
||||
}
|
||||
panel.addWidget(pluginId);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureTaskManagerPins(panel, launchersStr) {
|
||||
try {
|
||||
var ws = panel.widgets();
|
||||
for (var i = 0; i < ws.length; i++) {
|
||||
if (ws[i].type === 'org.kde.plasma.taskmanager') {
|
||||
var tm = ws[i];
|
||||
tm.currentConfigGroup = ['General'];
|
||||
tm.writeConfig('launchers', launchersStr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
var p = firstPanel();
|
||||
if (!p) return;
|
||||
|
||||
// License status widget
|
||||
ensureWidget(p, 'org.saikyo.license');
|
||||
|
||||
// Pinned launchers in task manager
|
||||
var launchers = [
|
||||
'applications:org.kde.discover.desktop',
|
||||
'applications:systemsettings.desktop',
|
||||
'applications:saikyo-license.desktop'
|
||||
].join(',');
|
||||
ensureTaskManagerPins(p, launchers);
|
||||
" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
if have_cmd qdbus; then
|
||||
add_panel_widgets_and_pins qdbus || true
|
||||
elif have_cmd qdbus6; then
|
||||
add_panel_widgets_and_pins qdbus6 || true
|
||||
fi
|
||||
|
||||
desktop_dir="${HOME}/Desktop"
|
||||
if [[ -r "${HOME}/.config/user-dirs.dirs" ]]; then
|
||||
d=$(grep -E '^XDG_DESKTOP_DIR=' "${HOME}/.config/user-dirs.dirs" | head -n 1 | cut -d= -f2- | tr -d '"')
|
||||
d=${d//\$HOME/${HOME}}
|
||||
if [[ -n "${d}" ]]; then desktop_dir="${d}"; fi
|
||||
fi
|
||||
|
||||
# Ensure KWallet doesn't require a pre-existing GPG key by default.
|
||||
# This prevents first-run failures in apps that request KWallet integration.
|
||||
mkdir -p "${XDG_CONFIG_HOME:-${HOME}/.config}" 2>/dev/null || true
|
||||
{
|
||||
echo "[Wallet]"
|
||||
echo "UseGpg=false"
|
||||
} > "${XDG_CONFIG_HOME:-${HOME}/.config}/kwalletrc" 2>/dev/null || true
|
||||
|
||||
# Set a default user avatar for new sessions (shown in KDE menus/panel).
|
||||
# Prefer PNG (KDE reliably picks it up); fallback to SVG.
|
||||
if [[ -r "${AVATAR_PNG}" ]]; then
|
||||
cp -f "${AVATAR_PNG}" "${HOME}/.face.icon" 2>/dev/null || true
|
||||
chmod 0644 "${HOME}/.face.icon" 2>/dev/null || true
|
||||
elif [[ -r "${LOGO_PATH}" ]]; then
|
||||
cp -f "${LOGO_PATH}" "${HOME}/.face.icon" 2>/dev/null || true
|
||||
chmod 0644 "${HOME}/.face.icon" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
mkdir -p "${desktop_dir}" 2>/dev/null || true
|
||||
if [[ -r /usr/share/applications/saikyo-license.desktop ]]; then
|
||||
cp -f /usr/share/applications/saikyo-license.desktop "${desktop_dir}/SAIKYO OS License.desktop" 2>/dev/null || true
|
||||
chmod 0755 "${desktop_dir}/SAIKYO OS License.desktop" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [[ -r /usr/share/applications/org.kde.discover.desktop ]]; then
|
||||
cp -f /usr/share/applications/org.kde.discover.desktop "${desktop_dir}/SAIKYO Discover.desktop" 2>/dev/null || true
|
||||
chmod 0755 "${desktop_dir}/SAIKYO Discover.desktop" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [[ -r /usr/share/applications/systemsettings.desktop ]]; then
|
||||
cp -f /usr/share/applications/systemsettings.desktop "${desktop_dir}/System Settings.desktop" 2>/dev/null || true
|
||||
chmod 0755 "${desktop_dir}/System Settings.desktop" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if have_cmd kdialog; then
|
||||
msg="Добро пожаловать в SAIKYO OS.\n\nНа этой системе действует пробный период 7 дней.\nПосле истечения пробного периода потребуется полная активация лицензии.\n\nРекомендуется выполнить активацию заранее."
|
||||
if kdialog --title "SAIKYO OS" --icon "${LOGO_PATH}" --yes-label "Открыть активацию" --no-label "Позже" --yesno "${msg}" 2>/dev/null; then
|
||||
if have_cmd saikyo-license-kde; then
|
||||
nohup saikyo-license-kde >/dev/null 2>&1 &
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
touch "${MARK_FILE}" 2>/dev/null || true
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIHmDCCB0WgAwIBAgIKYqt5lQAAAAADtjAKBggqhQMHAQEDAjCCASQxHjAcBgkq
|
||||
hkiG9w0BCQEWD2RpdEBtaW5zdnlhei5ydTELMAkGA1UEBhMCUlUxGDAWBgNVBAgM
|
||||
Dzc3INCc0L7RgdC60LLQsDEZMBcGA1UEBwwQ0LMuINCc0L7RgdC60LLQsDEuMCwG
|
||||
A1UECQwl0YPQu9C40YbQsCDQotCy0LXRgNGB0LrQsNGPLCDQtNC+0LwgNzEsMCoG
|
||||
A1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+0YHRgdC40LgxGDAWBgUq
|
||||
hQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqFAwOBAwEBEgwwMDc3MTA0NzQzNzUx
|
||||
LDAqBgNVBAMMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MB4X
|
||||
DTIwMDIwNTE0MDI0N1oXDTM1MDIwNTE0MDI0N1owggFtMSAwHgYJKoZIhvcNAQkB
|
||||
FhF1Y19ma0Byb3NrYXpuYS5ydTEZMBcGA1UECAwQ0LMuINCc0L7RgdC60LLQsDEa
|
||||
MBgGCCqFAwOBAwEBEgwwMDc3MTA1Njg3NjAxGDAWBgUqhQNkARINMTA0Nzc5NzAx
|
||||
OTgzMDFgMF4GA1UECQxX0JHQvtC70YzRiNC+0Lkg0JfQu9Cw0YLQvtGD0YHRgtC4
|
||||
0L3RgdC60LjQuSDQv9C10YDQtdGD0LvQvtC6LCDQtC4gNiwg0YHRgtGA0L7QtdC9
|
||||
0LjQtSAxMRUwEwYDVQQHDAzQnNC+0YHQutCy0LAxCzAJBgNVBAYTAlJVMTgwNgYD
|
||||
VQQKDC/QpNC10LTQtdGA0LDQu9GM0L3QvtC1INC60LDQt9C90LDRh9C10LnRgdGC
|
||||
0LLQvjE4MDYGA1UEAwwv0KTQtdC00LXRgNCw0LvRjNC90L7QtSDQutCw0LfQvdCw
|
||||
0YfQtdC50YHRgtCy0L4wZjAfBggqhQMHAQEBATATBgcqhQMCAiMBBggqhQMHAQEC
|
||||
AgNDAARA/G1/DjI9Hw+IV16X9pm0vTEj7uE8rv88OAAdmD7ddmRuYD6FLl491Zqv
|
||||
AxF94s7j5C2i/XC5OMkEhJQ6nKUI9KOCBAMwggP/MBIGA1UdEwEB/wQIMAYBAf8C
|
||||
AQAwUgYFKoUDZG8ESQxHItCa0YDQuNC/0YLQvtCf0YDQviBDU1AiINCy0LXRgNGB
|
||||
0LjRjyA0LjAgKNC40YHQv9C+0LvQvdC10L3QuNC1IDItQmFzZSkwJQYDVR0gBB4w
|
||||
HDAIBgYqhQNkcQEwCAYGKoUDZHECMAYGBFUdIAAwDgYDVR0PAQH/BAQDAgHGMIIB
|
||||
ZQYDVR0jBIIBXDCCAViAFMJU8bRr1Ey34G02tCOQ8f7DPJsGoYIBLKSCASgwggEk
|
||||
MR4wHAYJKoZIhvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgw
|
||||
FgYDVQQIDA83NyDQnNC+0YHQutCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy
|
||||
0LAxLjAsBgNVBAkMJdGD0LvQuNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8
|
||||
IDcxLDAqBgNVBAoMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4
|
||||
MRgwFgYFKoUDZAESDTEwNDc3MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEw
|
||||
NDc0Mzc1MSwwKgYDVQQDDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB
|
||||
0LjQuIIQTm1HiybyfWV/do4CXOPTkzAdBgNVHQ4EFgQU0GSWbXJA61h9JH+7IFvP
|
||||
w45setQwgZgGA1UdHwSBkDCBjTAtoCugKYYnaHR0cDovL3JlZXN0ci1wa2kucnUv
|
||||
Y2RwL2d1Y19nb3N0MTIuY3JsMC2gK6AphidodHRwOi8vY29tcGFueS5ydC5ydS9j
|
||||
ZHAvZ3VjX2dvc3QxMi5jcmwwLaAroCmGJ2h0dHA6Ly9yb3N0ZWxlY29tLnJ1L2Nk
|
||||
cC9ndWNfZ29zdDEyLmNybDBDBggrBgEFBQcBAQQ3MDUwMwYIKwYBBQUHMAKGJ2h0
|
||||
dHA6Ly9yZWVzdHItcGtpLnJ1L2NkcC9ndWNfZ29zdDEyLmNydDCB9QYFKoUDZHAE
|
||||
geswgegMNNCf0JDQmtCcIMKr0JrRgNC40L/RgtC+0J/RgNC+IEhTTcK7INCy0LXR
|
||||
gNGB0LjQuCAyLjAMQ9Cf0JDQmiDCq9CT0L7Qu9C+0LLQvdC+0Lkg0YPQtNC+0YHR
|
||||
gtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAwrsMNdCX0LDQutC70Y7Rh9C1
|
||||
0L3QuNC1IOKEliAxNDkvMy8yLzIvMjMg0L7RgiAwMi4wMy4yMDE4DDTQl9Cw0LrQ
|
||||
u9GO0YfQtdC90LjQtSDihJYgMTQ5LzcvNi8xMDUg0L7RgiAyNy4wNi4yMDE4MAoG
|
||||
CCqFAwcBAQMCA0EAYO7/gtED6JgCLK7ffDDrnggByLOMGafo2r5TakOU/aBSidSD
|
||||
a17cnrTflUw6REP8SrOa7o/tfCwpkbLJF2l6CQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEizCCA/egAwIBAgIVALl/bbS8diQY7sdeMXod8B1oPUzeMAoGCCqFAwcBAQMD
|
||||
MIIBMDEVMBMGBSqFA2QEEgo3NzEwNDc0Mzc1MRgwFgYFKoUDZAESDTEwNDc3MDIw
|
||||
MjY3MDExUzBRBgNVBAkMStCf0YDQtdGB0L3QtdC90YHQutCw0Y8g0L3QsNCx0LXR
|
||||
gNC10LbQvdCw0Y8sINC00L7QvCAxMCwg0YHRgtGA0L7QtdC90LjQtSAyMRkwFwYD
|
||||
VQQHDBDQsy4g0JzQvtGB0LrQstCwMRgwFgYDVQQIDA83NyDQnNC+0YHQutCy0LAx
|
||||
CzAJBgNVBAYTAlJVMSYwJAYDVQQKDB3QnNC40L3RhtC40YTRgNGLINCg0L7RgdGB
|
||||
0LjQuDE+MDwGA1UEAww10JzQuNC90YbQuNGE0YDRiyDQoNC+0YHRgdC40Lgg0J3Q
|
||||
o9CmINC60L7RgNC90LXQstC+0LkwHhcNMjUwNTI4MTU0MTQxWhcNNDAwNTI0MTU0
|
||||
MTQxWjCCATAxFTATBgUqhQNkBBIKNzcxMDQ3NDM3NTEYMBYGBSqFA2QBEg0xMDQ3
|
||||
NzAyMDI2NzAxMVMwUQYDVQQJDErQn9GA0LXRgdC90LXQvdGB0LrQsNGPINC90LDQ
|
||||
sdC10YDQtdC20L3QsNGPLCDQtNC+0LwgMTAsINGB0YLRgNC+0LXQvdC40LUgMjEZ
|
||||
MBcGA1UEBwwQ0LMuINCc0L7RgdC60LLQsDEYMBYGA1UECAwPNzcg0JzQvtGB0LrQ
|
||||
stCwMQswCQYDVQQGEwJSVTEmMCQGA1UECgwd0JzQuNC90YbQuNGE0YDRiyDQoNC+
|
||||
0YHRgdC40LgxPjA8BgNVBAMMNdCc0LjQvdGG0LjRhNGA0Ysg0KDQvtGB0YHQuNC4
|
||||
INCd0KPQpiDQutC+0YDQvdC10LLQvtC5MIGqMCEGCCqFAwcBAQECMBUGCSqFAwcB
|
||||
AgECAQYIKoUDBwEBAgMDgYQABIGAdCxt4xzP9ZCep1FqKXymiUBX5GVp9KNV6bCV
|
||||
sw+nV+E2/i2QKoTXkSrZwNdTAGfHaxRj0dNZjwljPyGRARTmBSG1CLvZvuwrla4O
|
||||
ki2HXmLtLmdZCmk0MVG/0qyK2Hg/cG9VU/YvHNSxbdWgfBpaIKEprR3511tD1Sui
|
||||
xEPhknSjgZcwgZQwEgYDVR0TAQH/BAgwBgEB/wIBBDAdBgNVHQ4EFgQU2EhWFsjb
|
||||
gUKVDHySi+o35xGlveowDgYDVR0PAQH/BAQDAgEGME8GA1UdIARIMEYwCgYIKoUD
|
||||
AhkBDgMwBgYEVR0gADAIBgYqhQNkcQEwCAYGKoUDZHECMAgGBiqFA2RxAzAIBgYq
|
||||
hQNkcQQwCAYGKoUDZHEFMAoGCCqFAwcBAQMDA4GBAG6v+ir5U37uMA9IEkhNMl95
|
||||
PSa6X0XsLrmxWFjHVQcPMGP5niDjF0pNDqxps40Ig2qqzGZXlFLxuu0w+xeuDfMn
|
||||
/AokEYCkWsMRhmzNEmVkLWggEaqBfHO+JEmAjwvyo6ggyvBdX8xQRpbcw9/Ugxx5
|
||||
h7F9deB2Q1ZcoFgcZmFl
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIHhjCCBzOgAwIBAgILALXxMtMAAAAAAVowCgYIKoUDBwEBAwIwggEkMR4wHAYJ
|
||||
KoZIhvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgwFgYDVQQI
|
||||
DA83NyDQnNC+0YHQutCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy0LAxLjAs
|
||||
BgNVBAkMJdGD0LvQuNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8IDcxLDAq
|
||||
BgNVBAoMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYF
|
||||
KoUDZAESDTEwNDc3MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEwNDc0Mzc1
|
||||
MSwwKgYDVQQDDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB0LjQuDAe
|
||||
Fw0xODExMTkxNTU2MDFaFw0zMzExMTkxNTU2MDFaMIIBbTEgMB4GCSqGSIb3DQEJ
|
||||
ARYRdWNfZmtAcm9za2F6bmEucnUxGTAXBgNVBAgMENCzLiDQnNC+0YHQutCy0LAx
|
||||
GjAYBggqhQMDgQMBARIMMDA3NzEwNTY4NzYwMRgwFgYFKoUDZAESDTEwNDc3OTcw
|
||||
MTk4MzAxYDBeBgNVBAkMV9CR0L7Qu9GM0YjQvtC5INCX0LvQsNGC0L7Rg9GB0YLQ
|
||||
uNC90YHQutC40Lkg0L/QtdGA0LXRg9C70L7Quiwg0LQuIDYsINGB0YLRgNC+0LXQ
|
||||
vdC40LUgMTEVMBMGA1UEBwwM0JzQvtGB0LrQstCwMQswCQYDVQQGEwJSVTE4MDYG
|
||||
A1UECgwv0KTQtdC00LXRgNCw0LvRjNC90L7QtSDQutCw0LfQvdCw0YfQtdC50YHR
|
||||
gtCy0L4xODA2BgNVBAMML9Ck0LXQtNC10YDQsNC70YzQvdC+0LUg0LrQsNC30L3Q
|
||||
sNGH0LXQudGB0YLQstC+MGYwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEB
|
||||
AgIDQwAEQBHF4BLi2ABpI2cZNBIql/rippw/0qqoi4S2NvJHSGbC694tvfJqOmGG
|
||||
UCD0b1kUlEq6ueFAtF7gOICquPj+DcmjggPwMIID7DASBgNVHRMBAf8ECDAGAQH/
|
||||
AgEAMD8GBSqFA2RvBDYMNNCh0JrQl9CYICLQmtGA0LjQv9GC0L7Qn9GA0L4gQ1NQ
|
||||
IiAo0LLQtdGA0YHQuNGPIDQuMCkwJQYDVR0gBB4wHDAIBgYqhQNkcQEwCAYGKoUD
|
||||
ZHECMAYGBFUdIAAwDgYDVR0PAQH/BAQDAgHGMIIBZQYDVR0jBIIBXDCCAViAFMJU
|
||||
8bRr1Ey34G02tCOQ8f7DPJsGoYIBLKSCASgwggEkMR4wHAYJKoZIhvcNAQkBFg9k
|
||||
aXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgwFgYDVQQIDA83NyDQnNC+0YHQ
|
||||
utCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy0LAxLjAsBgNVBAkMJdGD0LvQ
|
||||
uNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8IDcxLDAqBgNVBAoMI9Cc0LjQ
|
||||
vdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYFKoUDZAESDTEwNDc3
|
||||
MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEwNDc0Mzc1MSwwKgYDVQQDDCPQ
|
||||
nNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB0LjQuIIQTm1HiybyfWV/do4C
|
||||
XOPTkzAdBgNVHQ4EFgQUwNbWCn1rfsmOObzaifqvlCxYWo0wgZgGA1UdHwSBkDCB
|
||||
jTAtoCugKYYnaHR0cDovL3JlZXN0ci1wa2kucnUvY2RwL2d1Y19nb3N0MTIuY3Js
|
||||
MC2gK6AphidodHRwOi8vY29tcGFueS5ydC5ydS9jZHAvZ3VjX2dvc3QxMi5jcmww
|
||||
LaAroCmGJ2h0dHA6Ly9yb3N0ZWxlY29tLnJ1L2NkcC9ndWNfZ29zdDEyLmNybDBD
|
||||
BggrBgEFBQcBAQQ3MDUwMwYIKwYBBQUHMAKGJ2h0dHA6Ly9yZWVzdHItcGtpLnJ1
|
||||
L2NkcC9ndWNfZ29zdDEyLmNydDCB9QYFKoUDZHAEgeswgegMNNCf0JDQmtCcIMKr
|
||||
0JrRgNC40L/RgtC+0J/RgNC+IEhTTcK7INCy0LXRgNGB0LjQuCAyLjAMQ9Cf0JDQ
|
||||
miDCq9CT0L7Qu9C+0LLQvdC+0Lkg0YPQtNC+0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQ
|
||||
uSDRhtC10L3RgtGAwrsMNdCX0LDQutC70Y7Rh9C10L3QuNC1IOKEliAxNDkvMy8y
|
||||
LzIvMjMg0L7RgiAwMi4wMy4yMDE4DDTQl9Cw0LrQu9GO0YfQtdC90LjQtSDihJYg
|
||||
MTQ5LzcvNi8xMDUg0L7RgiAyNy4wNi4yMDE4MAoGCCqFAwcBAQMCA0EALtT076gA
|
||||
jyE3aXhaCfJUQCH8xyUy4/It0btS24K3tKJZYLaYahNjbNNqcEFptDfn6I0c89xz
|
||||
oKhFJDPHvjy/sg==
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFwjCCA6qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx
|
||||
PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu
|
||||
ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg
|
||||
Q0EwHhcNMjIwMzAxMjEwNDE1WhcNMzIwMjI3MjEwNDE1WjBwMQswCQYDVQQGEwJS
|
||||
VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg
|
||||
YW5kIENvbW11bmljYXRpb25zMSAwHgYDVQQDDBdSdXNzaWFuIFRydXN0ZWQgUm9v
|
||||
dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMfFOZ8pUAL3+r2n
|
||||
qqE0Zp52selXsKGFYoG0GM5bwz1bSFtCt+AZQMhkWQheI3poZAToYJu69pHLKS6Q
|
||||
XBiwBC1cvzYmUYKMYZC7jE5YhEU2bSL0mX7NaMxMDmH2/NwuOVRj8OImVa5s1F4U
|
||||
zn4Kv3PFlDBjjSjXKVY9kmjUBsXQrIHeaqmUIsPIlNWUnimXS0I0abExqkbdrXbX
|
||||
YwCOXhOO2pDUx3ckmJlCMUGacUTnylyQW2VsJIyIGA8V0xzdaeUXg0VZ6ZmNUr5Y
|
||||
Ber/EAOLPb8NYpsAhJe2mXjMB/J9HNsoFMBFJ0lLOT/+dQvjbdRZoOT8eqJpWnVD
|
||||
U+QL/qEZnz57N88OWM3rabJkRNdU/Z7x5SFIM9FrqtN8xewsiBWBI0K6XFuOBOTD
|
||||
4V08o4TzJ8+Ccq5XlCUW2L48pZNCYuBDfBh7FxkB7qDgGDiaftEkZZfApRg2E+M9
|
||||
G8wkNKTPLDc4wH0FDTijhgxR3Y4PiS1HL2Zhw7bD3CbslmEGgfnnZojNkJtcLeBH
|
||||
BLa52/dSwNU4WWLubaYSiAmA9IUMX1/RpfpxOxd4Ykmhz97oFbUaDJFipIggx5sX
|
||||
ePAlkTdWnv+RWBxlJwMQ25oEHmRguNYf4Zr/Rxr9cS93Y+mdXIZaBEE0KS2iLRqa
|
||||
OiWBki9IMQU4phqPOBAaG7A+eP8PAgMBAAGjZjBkMB0GA1UdDgQWBBTh0YHlzlpf
|
||||
BKrS6badZrHF+qwshzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qwshzAS
|
||||
BgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
|
||||
AAOCAgEAALIY1wkilt/urfEVM5vKzr6utOeDWCUczmWX/RX4ljpRdgF+5fAIS4vH
|
||||
tmXkqpSCOVeWUrJV9QvZn6L227ZwuE15cWi8DCDal3Ue90WgAJJZMfTshN4OI8cq
|
||||
W9E4EG9wglbEtMnObHlms8F3CHmrw3k6KmUkWGoa+/ENmcVl68u/cMRl1JbW2bM+
|
||||
/3A+SAg2c6iPDlehczKx2oa95QW0SkPPWGuNA/CE8CpyANIhu9XFrj3RQ3EqeRcS
|
||||
AQQod1RNuHpfETLU/A2gMmvn/w/sx7TB3W5BPs6rprOA37tutPq9u6FTZOcG1Oqj
|
||||
C/B7yTqgI7rbyvox7DEXoX7rIiEqyNNUguTk/u3SZ4VXE2kmxdmSh3TQvybfbnXV
|
||||
4JbCZVaqiZraqc7oZMnRoWrXRG3ztbnbes/9qhRGI7PqXqeKJBztxRTEVj8ONs1d
|
||||
WN5szTwaPIvhkhO3CO5ErU2rVdUr89wKpNXbBODFKRtgxUT70YpmJ46VVaqdAhOZ
|
||||
D9EUUn4YaeLaS8AjSF/h7UkjOibNc4qVDiPP+rkehFWM66PVnP1Msh93tc+taIfC
|
||||
EYVMxjh8zNbFuoc7fzvvrFILLe7ifvEIUqSVIC/AzplM/Jxw7buXFeGP1qVCBEHq
|
||||
391d/9RAfaZ12zkwFsl+IKwE/OZxW8AHa9i1p4GO0YSNuczzEm4=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFFDCCBMGgAwIBAgIQTm1HiybyfWV/do4CXOPTkzAKBggqhQMHAQEDAjCCASQx
|
||||
HjAcBgkqhkiG9w0BCQEWD2RpdEBtaW5zdnlhei5ydTELMAkGA1UEBhMCUlUxGDAW
|
||||
BgNVBAgMDzc3INCc0L7RgdC60LLQsDEZMBcGA1UEBwwQ0LMuINCc0L7RgdC60LLQ
|
||||
sDEuMCwGA1UECQwl0YPQu9C40YbQsCDQotCy0LXRgNGB0LrQsNGPLCDQtNC+0Lwg
|
||||
NzEsMCoGA1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+0YHRgdC40Lgx
|
||||
GDAWBgUqhQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqFAwOBAwEBEgwwMDc3MTA0
|
||||
NzQzNzUxLDAqBgNVBAMMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQ
|
||||
uNC4MB4XDTE4MDcwNjEyMTgwNloXDTM2MDcwMTEyMTgwNlowggEkMR4wHAYJKoZI
|
||||
hvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRgwFgYDVQQIDA83
|
||||
NyDQnNC+0YHQutCy0LAxGTAXBgNVBAcMENCzLiDQnNC+0YHQutCy0LAxLjAsBgNV
|
||||
BAkMJdGD0LvQuNGG0LAg0KLQstC10YDRgdC60LDRjywg0LTQvtC8IDcxLDAqBgNV
|
||||
BAoMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYFKoUD
|
||||
ZAESDTEwNDc3MDIwMjY3MDExGjAYBggqhQMDgQMBARIMMDA3NzEwNDc0Mzc1MSww
|
||||
KgYDVQQDDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB0LjQuDBmMB8G
|
||||
CCqFAwcBAQEBMBMGByqFAwICIwEGCCqFAwcBAQICA0MABEB1OSpFp7milX33EP0i
|
||||
kge6HbZacYp9fVj8sUa5RWFXrB27SKX5SvtIGepqKev69RSYeHHKR+jT9YX2NuSK
|
||||
9wONo4IBwjCCAb4wgfUGBSqFA2RwBIHrMIHoDDTQn9CQ0JrQnCDCq9Ca0YDQuNC/
|
||||
0YLQvtCf0YDQviBIU03CuyDQstC10YDRgdC40LggMi4wDEPQn9CQ0JogwqvQk9C+
|
||||
0LvQvtCy0L3QvtC5INGD0LTQvtGB0YLQvtCy0LXRgNGP0Y7RidC40Lkg0YbQtdC9
|
||||
0YLRgMK7DDXQl9Cw0LrQu9GO0YfQtdC90LjQtSDihJYgMTQ5LzMvMi8yLzIzINC+
|
||||
0YIgMDIuMDMuMjAxOAw00JfQsNC60LvRjtGH0LXQvdC40LUg4oSWIDE0OS83LzYv
|
||||
MTA1INC+0YIgMjcuMDYuMjAxODA/BgUqhQNkbwQ2DDTQn9CQ0JrQnCDCq9Ca0YDQ
|
||||
uNC/0YLQvtCf0YDQviBIU03CuyDQstC10YDRgdC40LggMi4wMEMGA1UdIAQ8MDow
|
||||
CAYGKoUDZHEBMAgGBiqFA2RxAjAIBgYqhQNkcQMwCAYGKoUDZHEEMAgGBiqFA2Rx
|
||||
BTAGBgRVHSAAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||
DgQWBBTCVPG0a9RMt+BtNrQjkPH+wzybBjAKBggqhQMHAQEDAgNBAJr6/eI7rHL7
|
||||
+FsQnoH2i6DVxqalbIxLKj05edpZGPLLb6B2PTAMya7pSt9hb8QnFABgsR4IE5gT
|
||||
4VVkDWbX/n4=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"DisableQUIC": true,
|
||||
"SSLVersionMin": "tls1",
|
||||
"SSLErrorOverrideAllowed": false,
|
||||
"EnableOnlineRevocationChecks": true,
|
||||
"NetworkPredictionOptions": 2
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
99
saikyo-branding/assets/kde/SaikyoDark.colors
Normal file
99
saikyo-branding/assets/kde/SaikyoDark.colors
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
[General]
|
||||
ColorScheme=SAIKYO Dark
|
||||
Name=SAIKYO Dark
|
||||
shadeSortColumn=true
|
||||
|
||||
[Colors:Button]
|
||||
BackgroundAlternate=29,85,184
|
||||
BackgroundNormal=11,29,68
|
||||
DecorationFocus=30,85,184
|
||||
DecorationHover=36,110,220
|
||||
ForegroundActive=232,241,255
|
||||
ForegroundInactive=183,208,255
|
||||
ForegroundLink=110,160,255
|
||||
ForegroundNegative=255,138,138
|
||||
ForegroundNeutral=255,210,110
|
||||
ForegroundNormal=232,241,255
|
||||
ForegroundPositive=120,230,170
|
||||
ForegroundVisited=150,120,255
|
||||
|
||||
[Colors:Complementary]
|
||||
BackgroundAlternate=16,34,68
|
||||
BackgroundNormal=9,18,35
|
||||
DecorationFocus=30,85,184
|
||||
DecorationHover=36,110,220
|
||||
ForegroundActive=232,241,255
|
||||
ForegroundInactive=183,208,255
|
||||
ForegroundLink=110,160,255
|
||||
ForegroundNegative=255,138,138
|
||||
ForegroundNeutral=255,210,110
|
||||
ForegroundNormal=232,241,255
|
||||
ForegroundPositive=120,230,170
|
||||
ForegroundVisited=150,120,255
|
||||
|
||||
[Colors:Selection]
|
||||
BackgroundAlternate=30,85,184
|
||||
BackgroundNormal=30,85,184
|
||||
DecorationFocus=30,85,184
|
||||
DecorationHover=36,110,220
|
||||
ForegroundActive=255,255,255
|
||||
ForegroundInactive=255,255,255
|
||||
ForegroundLink=255,255,255
|
||||
ForegroundNegative=255,255,255
|
||||
ForegroundNeutral=255,255,255
|
||||
ForegroundNormal=255,255,255
|
||||
ForegroundPositive=255,255,255
|
||||
ForegroundVisited=255,255,255
|
||||
|
||||
[Colors:Tooltip]
|
||||
BackgroundAlternate=16,34,68
|
||||
BackgroundNormal=11,29,68
|
||||
DecorationFocus=30,85,184
|
||||
DecorationHover=36,110,220
|
||||
ForegroundActive=232,241,255
|
||||
ForegroundInactive=183,208,255
|
||||
ForegroundLink=110,160,255
|
||||
ForegroundNegative=255,138,138
|
||||
ForegroundNeutral=255,210,110
|
||||
ForegroundNormal=232,241,255
|
||||
ForegroundPositive=120,230,170
|
||||
ForegroundVisited=150,120,255
|
||||
|
||||
[Colors:View]
|
||||
BackgroundAlternate=9,18,35
|
||||
BackgroundNormal=5,10,20
|
||||
DecorationFocus=30,85,184
|
||||
DecorationHover=36,110,220
|
||||
ForegroundActive=232,241,255
|
||||
ForegroundInactive=183,208,255
|
||||
ForegroundLink=110,160,255
|
||||
ForegroundNegative=255,138,138
|
||||
ForegroundNeutral=255,210,110
|
||||
ForegroundNormal=232,241,255
|
||||
ForegroundPositive=120,230,170
|
||||
ForegroundVisited=150,120,255
|
||||
|
||||
[Colors:Window]
|
||||
BackgroundAlternate=16,34,68
|
||||
BackgroundNormal=7,26,58
|
||||
DecorationFocus=30,85,184
|
||||
DecorationHover=36,110,220
|
||||
ForegroundActive=232,241,255
|
||||
ForegroundInactive=183,208,255
|
||||
ForegroundLink=110,160,255
|
||||
ForegroundNegative=255,138,138
|
||||
ForegroundNeutral=255,210,110
|
||||
ForegroundNormal=232,241,255
|
||||
ForegroundPositive=120,230,170
|
||||
ForegroundVisited=150,120,255
|
||||
|
||||
[KDE]
|
||||
contrast=4
|
||||
|
||||
[WM]
|
||||
activeBackground=7,26,58
|
||||
activeBlend=7,26,58
|
||||
activeForeground=232,241,255
|
||||
inactiveBackground=5,10,20
|
||||
inactiveBlend=5,10,20
|
||||
inactiveForeground=183,208,255
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
[kdeglobals][General]
|
||||
ColorScheme=SaikyoDark
|
||||
|
||||
[kdeglobals][KDE]
|
||||
SingleClick=false
|
||||
|
||||
[plasmarc][Theme]
|
||||
name=breeze-dark
|
||||
|
||||
[ksplashrc][KSplash]
|
||||
Engine=KSplashQML
|
||||
Theme=Saikyo
|
||||
|
||||
[sddm.conf][Theme]
|
||||
Current=Saikyo
|
||||
|
||||
[Wallpaper][org.kde.image][General]
|
||||
Image=file:///usr/share/wallpapers/Saikyo/contents/images/saikyo-default.svg
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user