perf: merge with remote

pull/14788/head
ibuler 2025-01-07 17:05:52 +08:00
parent ca10fc5f20
commit ce322a46f7
4 changed files with 145 additions and 9 deletions

2
.gitattributes vendored
View File

@ -1,4 +1,4 @@
*.mmdb filter=lfs diff=lfs merge=lfs -text *.mmdb filter=lfs diff=lfs merge=lfs -text
*.mo filter=lfs diff=lfs merge=lfs -text *.mo filter=lfs diff=lfs merge=lfs -text
*.ipdb filter=lfs diff=lfs merge=lfs -text *.ipdb filter=lfs diff=lfs merge=lfs -text
leak_passwords.db filter=lfs diff=lfs merge=lfs -text

View File

@ -1,6 +1,8 @@
import os
import re import re
from collections import defaultdict import sqlite3
from django.conf import settings
from django.utils import timezone from django.utils import timezone
from accounts.models import Account, AccountRisk, RiskChoice from accounts.models import Account, AccountRisk, RiskChoice
@ -104,13 +106,136 @@ def check_account_secrets(accounts, assets):
class CheckAccountManager(BaseManager): class CheckAccountManager(BaseManager):
batch_size = 100 batch_size = 100
tmpl = 'Checked the status of account %s: %s'
def __init__(self, execution): def __init__(self, execution):
super().__init__(execution) super().__init__(execution)
self.accounts = [] self.accounts = []
self.assets = [] self.assets = []
self.global_origin_risks_dict = dict()
self.db_conn = None
self.db_cursor = None
def init_leak_password_db(self):
default_path = os.path.join(
settings.APPS_DIR, 'accounts', 'automations', 'check_account'
)
create_table = '''
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
asset_id TEXT, asset_name TEXT, name TEXT,
username TEXT, password TEXT
)
'''
if (settings.LEAK_PASSWORD_DB_PATH
and os.path.exists(settings.LEAK_PASSWORD_DB_PATH)):
db_path = settings.LEAK_PASSWORD_DB_PATH
else:
db_path = os.path.join(default_path, 'leak_passwords.db')
self.db_conn = sqlite3.connect(db_path)
self.db_cursor = self.db_conn.cursor()
self.db_cursor.execute(create_table)
def drop_account_table(self):
sql = 'DROP TABLE IF EXISTS accounts'
self.db_cursor.execute(sql)
self.db_conn.commit()
def close_db(self):
try:
self.db_cursor.close()
self.db_conn.close()
except Exception: # noqa
pass
@staticmethod
def create_or_update_risk(risks, origin_risks_dict):
now = timezone.now().isoformat()
for d in risks:
key = f'{d["account"].asset_id}_{d["account"].username}_{d["risk"]}'
origin_risk = origin_risks_dict.get(key)
if origin_risk:
origin_risk.details.append({"datetime": now, 'type': 'refind'})
update_risk(origin_risk)
else:
create_risk({
"asset": d["account"].asset,
"username": d["account"].username,
"risk": d["risk"],
"details": [{"datetime": now, 'type': 'init'}],
})
def is_leak_password(self, password):
sql = 'SELECT 1 FROM leak_passwords WHERE password = ? LIMIT 1'
self.db_cursor.execute(sql, (password,))
return self.db_cursor.fetchone() is not None
def check_account_secrets(self, accounts, assets):
risks = []
for account in accounts:
if not account.secret:
print(self.tmpl % (account, "no secret"))
self.risk_record('no_secret', account)
continue
if is_weak_password(account.secret):
key = RiskChoice.weak_password
print(self.tmpl % (account, color_fmt(key.value, "red")))
risks.append(self.risk_record(key, account))
elif self.is_leak_password(account.secret):
key = RiskChoice.leaked_password
print(self.tmpl % (account, color_fmt(key.value, "red")))
risks.append(self.risk_record(key, account))
else:
sql = ("INSERT INTO accounts (name, username, password, asset_id, asset_name) "
"VALUES (?, ?, ?, ?, ?)")
self.db_cursor.execute(
sql, [
account.name, account.username, account.secret,
str(account.asset_id), account.asset.name
]
)
self.db_conn.commit()
origin_risks = AccountRisk.objects.filter(asset__in=assets)
origin_risks_dict = {f"{r.asset_id}_{r.username}_{r.risk}": r for r in origin_risks}
self.global_origin_risks_dict.update(origin_risks_dict)
self.create_or_update_risk(risks, origin_risks_dict)
def risk_record(self, key, account):
self.summary[key] += 1
self.result[key].append({
'asset': str(account.asset), 'username': account.username,
})
return {'account': account, 'risk': key}
def check_repeat_secrets(self):
risks = []
sql = '''
SELECT name, username, password, asset_id, asset_name,
CASE WHEN password IN (
SELECT password FROM accounts GROUP BY password HAVING COUNT(*) > 1
) THEN 1 ELSE 0 END AS is_repeated FROM accounts
'''
self.db_cursor.execute(sql)
for results in self.db_cursor.fetchall():
name, username, *_, asset_id, asset_name, is_repeat = results
account = Account(asset_id=asset_id, username=username, name=name)
account_display = f'{name}({asset_name})'
if is_repeat:
key = RiskChoice.repeated_password
print(self.tmpl % (account_display, color_fmt(key.value, "red")))
risks.append(self.risk_record(key, account))
else:
key = 'ok'
print(self.tmpl % (account_display, color_fmt("ok", "green")))
self.risk_record(key, account)
self.create_or_update_risk(risks, self.global_origin_risks_dict)
def pre_run(self): def pre_run(self):
self.init_leak_password_db()
self.assets = self.execution.get_all_assets() self.assets = self.execution.get_all_assets()
self.execution.date_start = timezone.now() self.execution.date_start = timezone.now()
self.execution.save(update_fields=["date_start"]) self.execution.save(update_fields=["date_start"])
@ -118,19 +243,22 @@ class CheckAccountManager(BaseManager):
def do_run(self, *args, **kwargs): def do_run(self, *args, **kwargs):
for engine in self.execution.snapshot.get("engines", []): for engine in self.execution.snapshot.get("engines", []):
if engine == "check_account_secret": if engine == "check_account_secret":
handle = check_account_secrets batch_handle = self.check_account_secrets
global_handle = self.check_repeat_secrets
else: else:
continue continue
for i in range(0, len(self.assets), self.batch_size): for i in range(0, len(self.assets), self.batch_size):
_assets = self.assets[i: i + self.batch_size] _assets = self.assets[i: i + self.batch_size]
accounts = Account.objects.filter(asset__in=_assets) accounts = Account.objects.filter(asset__in=_assets)
summary, result = handle(accounts, _assets) batch_handle(accounts, _assets)
for k, v in summary.items(): global_handle()
self.summary[k] = self.summary.get(k, 0) + v
for k, v in result.items(): def post_run(self):
self.result[k].extend(v) super().post_run()
self.drop_account_table()
self.close_db()
def get_report_subject(self): def get_report_subject(self):
return "Check account report of %s" % self.execution.id return "Check account report of %s" % self.execution.id
@ -140,10 +268,14 @@ class CheckAccountManager(BaseManager):
def print_summary(self): def print_summary(self):
tmpl = ( tmpl = (
"\n---\nSummary: \nok: %s, weak password: %s, no secret: %s, using time: %ss" "\n---\nSummary: \nok: %s, weak password: %s, leaked password: %s, "
"repeated password: %s, no secret: %s, using time: %ss"
% ( % (
self.summary["ok"], self.summary["ok"],
self.summary[RiskChoice.weak_password], self.summary[RiskChoice.weak_password],
self.summary[RiskChoice.leaked_password],
self.summary[RiskChoice.repeated_password],
self.summary["no_secret"], self.summary["no_secret"],
int(self.duration), int(self.duration),
) )

View File

@ -50,6 +50,8 @@ class RiskChoice(TextChoices):
long_time_password = 'long_time_password', _('Long time no change') # 好久没改密码的账号, 改密码 long_time_password = 'long_time_password', _('Long time no change') # 好久没改密码的账号, 改密码
weak_password = 'weak_password', _('Weak password') # 弱密码, 改密 weak_password = 'weak_password', _('Weak password') # 弱密码, 改密
leaked_password = 'leaked_password', _('Leaked password') # 可能泄露的密码, 改密
repeated_password = 'repeated_password', _('Repeated password') # 重复度高的密码, 改密
password_error = 'password_error', _('Password error') # 密码错误, 修改账号 password_error = 'password_error', _('Password error') # 密码错误, 修改账号
no_admin_account = 'no_admin_account', _('No admin account') # 无管理员账号, 设置账号 no_admin_account = 'no_admin_account', _('No admin account') # 无管理员账号, 设置账号
others = 'others', _('Others') # 其他风险, 确认 others = 'others', _('Others') # 其他风险, 确认

View File

@ -195,3 +195,5 @@ DJANGO_REDIS_SCAN_ITERSIZE = 1000
# GM DEVICE # GM DEVICE
PIICO_DEVICE_ENABLE = CONFIG.PIICO_DEVICE_ENABLE PIICO_DEVICE_ENABLE = CONFIG.PIICO_DEVICE_ENABLE
PIICO_DRIVER_PATH = CONFIG.PIICO_DRIVER_PATH PIICO_DRIVER_PATH = CONFIG.PIICO_DRIVER_PATH
LEAK_PASSWORD_DB_PATH = CONFIG.LEAK_PASSWORD_DB_PATH