2 Commits
Author SHA1 Message Date
coderNini f1d979640f Set up logging docker container, Grafana with Loki data source, and Grafana dashboard 2025-11-20 22:38:17 -08:00
nini.b-612 0e6ecf1f16 Update WordPress-Docker/docker-compose.yml
Added potential Grafana Loki installation code (commented it out for now just to be safe). Some changes can be made to the current commented out code, as recommended by ChatGPT.
2025-11-04 19:01:58 +00:00
18 changed files with 73 additions and 2726821 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

BIN
View File
Binary file not shown.
Binary file not shown.
+42
View File
@@ -1,11 +1,49 @@
version: '3.8'
services:
loki:
image: grafana/loki:3.0.0
container_name: loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
restart: unless-stopped
grafana:
image: grafana/grafana:10.4.0
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=ocfsecure
depends_on:
- loki
restart: unless-stopped
alloy:
image: grafana/alloy:latest
container_name: alloy
volumes:
- ./alloy-config.yml:/etc/alloy/config.alloy
- ./logs:/var/log/scanner
ports:
- "12345:12345" # optional web UI/debug
command: run /etc/alloy/config.alloy
environment:
- ALLOY_ENABLE_WEB=true
depends_on:
- loki
restart: unless-stopped
apache:
build:
context: ./apache
ports:
- "8080:80"
networks:
- shared_net
- default
volumes:
- userdata:/home
depends_on:
@@ -52,3 +90,7 @@ volumes:
userdata:
db_data:
wp_data:
networks:
shared_net:
external: true
+2
View File
@@ -0,0 +1,2 @@
Username: admin
Password: wp_graf_pass
@@ -0,0 +1,29 @@
version: "3.8"
networks:
shared_net:
external: true
services:
loki:
image: grafana/loki:3.0.0
container_name: loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
- shared_net
grafana:
image: grafana/grafana:10.4.0
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
depends_on:
- loki
networks:
- shared_net
-40
View File
@@ -1,40 +0,0 @@
import json
import matplotlib.pyplot as plt
json_path = "wordfence_vulnerabilities.json"
def load_cvss_scores(path):
"""Load JSON data and extract all CVSS scores into a list"""
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
scores = []
for entry in data:
if "cvss_score" in entry and isinstance(entry["cvss_score"], (int, float)):
scores.append(entry["cvss_score"])
return scores
def plot_hist(scores):
"""Plot a histogram of CVSS scores"""
plt.figure(figsize=(8, 5))
plt.hist(scores, bins=10, edgecolor="black", color="skyblue")
plt.title("Distribution of CVSS Scores in Vulnerability Data")
plt.xlabel("CVSS Score")
plt.ylabel("Number of Vulnerabilities")
plt.grid(alpha=0.4)
plt.savefig("cvss_distribution.png")
plt.show()
if __name__ == "__main__":
scores = load_cvss_scores(json_path)
print(f"Loaded {len(scores)} CVSS scores.")
print("Sample:", scores[:10])
if scores:
plot_hist(scores)
else:
print("error")
-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env python3
import json
from collections import Counter
import matplotlib.pyplot as plt
path = "wordfence_vulnerabilities.json"
# Load JSON data
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
vuln_types = []
for entry in data:
title = entry.get("title")
if not isinstance(title, str):
continue
# Split on the first dash (" - ") and take the right-hand side
if " - " in title:
vuln = title.split(" - ", 1)[1].strip()
vuln_types.append(vuln)
# Count occurrences
counts = Counter(vuln_types)
top25 = counts.most_common(25)
total = sum(counts.values())
if not vuln_types:
print("error")
exit()
# Print table of top 25
print("\nTop 25 Vulnerability Types:\n")
for name, count in top25:
rel = (count / total) * 100
print(f"{name:60} {count:5d} ({rel:5.2f}%)")
# Visualization
labels, values = zip(*top25)
plt.figure(figsize=(10, 6))
plt.barh(labels[::-1], values[::-1], color="cornflowerblue")
plt.title("Top 25 Most Common Vulnerability Types")
plt.xlabel("Count")
plt.ylabel("Vulnerability Type")
plt.tight_layout()
plt.show()
-1
View File
@@ -1 +0,0 @@
requests
-89
View File
@@ -1,89 +0,0 @@
#!/usr/bin/env python3
import json
import os
# vars
JSON_FILE = "wordfence_vulnerabilities.json"
BAD_LIST = []
JSON_OUT = "matches.json"
def load_local(path):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def get_entries(data):
# try to find the main list of vulnerabilities
if isinstance(data, dict):
if "vulnerabilities" in data:
return data["vulnerabilities"]
for value in data.values():
if isinstance(value, list):
return value
return []
elif isinstance(data, list):
return data
else:
return []
def main():
if os.path.exists(JSON_FILE):
print("Loading ...", JSON_FILE)
data = load_local(JSON_FILE)
else:
print("Error: JSON file not found:", JSON_FILE)
return
entries = get_entries(data)
bad_set = set(BAD_LIST)
hits = []
for item in entries:
if not isinstance(item, dict):
continue
if item.get("type") != "plugin":
continue
slug = (item.get("slug") or "").strip()
if not slug:
continue
if slug in bad_set:
hit = {
"slug": slug,
"title": item.get("title"),
"cve": item.get("cve"),
"severity": item.get("severity"),
"cvss_score": item.get("cvss_score"),
"cve_link": item.get("cve_link"),
"raw": item
}
hits.append(hit)
if not hits:
print("No matches at all")
else:
print("Found", len(hits), "matches:")
for h in hits:
print(
"-",
h["slug"],
"| CVE:", h.get("cve"),
"| sev:", h.get("severity"),
"| CVSS:", h.get("cvss_score")
)
print(" link:", h.get("cve_link"))
# save
with open(JSON_OUT, "w", encoding="utf-8") as f:
json.dump(hits, f, ensure_ascii=False, indent=2)
print("Saved to", JSON_OUT)
if __name__ == "__main__":
main()
-2
View File
@@ -1,2 +0,0 @@
get_CVE.py **MUST** be ran before cve_scanner_new1 can be ran since it runs using a list that is fetched daily.
CVE_scanner.py runs on a static list called wordfence_vulnerabilities.json that is stored in the same directory, which is an old list of major vulnerabilities.
-77
View File
@@ -1,77 +0,0 @@
#!/usr/bin/env python3
import json
import os
JSON_FILE = "wordfence_CVE.json"
JSON_OUT = "malware.json"
BAD_PLUGINS = []
def load_file(path):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def main():
if not os.path.exists(JSON_FILE):
print("json file not found:", JSON_FILE)
return
data = load_file(JSON_FILE)
bad_set = set()
for n in BAD_PLUGINS:
if isinstance(n, str):
n = n.strip()
if n:
bad_set.add(n)
hits = []
for key, item in data.items():
if not isinstance(item, dict):
continue
software_list = item.get("software") or []
for s in software_list:
if not isinstance(s, dict):
continue
if s.get("type") != "plugin":
continue
name = (s.get("name") or "").strip()
if name not in bad_set:
continue
affected = s.get("affected_versions") or {}
if not affected:
continue
refs = item.get("references") or []
ref = refs[0] if refs else None
hit = {
"id": item.get("id"),
"title": item.get("title"),
"name": name,
"slug": s.get("slug"),
"affected_versions": list(affected.keys()),
"link": ref,
"raw": item
}
hits.append(hit)
if not hits:
print("no matches found")
else:
print("found", len(hits), "matches")
for h in hits:
print("-", h["name"], "|", ", ".join(h["affected_versions"]))
with open(JSON_OUT, "w", encoding="utf-8") as f:
json.dump(hits, f, ensure_ascii=False, indent=2)
print("saved json to", JSON_OUT)
if __name__ == "__main__":
main()
-22
View File
@@ -1,22 +0,0 @@
#!/usr/bin/env python3
import requests
import os
URL = "https://www.wordfence.com/api/intelligence/v2/vulnerabilities/scanner"
OUT_FILE = "wordfence_CVE.json"
def main():
print("API request.....")
r = requests.get(URL, timeout=60)
r.raise_for_status()
script_dir = os.path.dirname(os.path.abspath(__file__))
out_path = os.path.join(script_dir, OUT_FILE)
with open(out_path, "w", encoding="utf-8") as f:
f.write(r.text)
print("File Saved!")
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-520306
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff