Update public source

This commit is contained in:
2026-10-07 09:13:34 +03:00
parent f231cc5b1f
commit ea8ca2a613
5 changed files with 332 additions and 12 deletions
+47
View File
@@ -41,6 +41,27 @@ class MethodsRepository:
), ),
) )
def update_method(
self,
method_id: str,
name: str,
description: str | None = None,
) -> None:
self._conn.execute(
"""
UPDATE crack_methods
SET
name = ?,
description = ?
WHERE id = ?
""",
(
name,
description,
method_id,
),
)
def get_method(self, method_id: str) -> sqlite3.Row | None: def get_method(self, method_id: str) -> sqlite3.Row | None:
row = self._conn.execute( row = self._conn.execute(
""" """
@@ -99,6 +120,32 @@ class MethodsRepository:
), ),
) )
def update_method_version(
self,
method_id: str,
version: int,
definition: dict[str, Any],
) -> None:
self._conn.execute(
"""
UPDATE crack_method_versions
SET
definition_json = ?
WHERE method_id = ?
AND version = ?
""",
(
json.dumps(
definition,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
),
method_id,
version,
),
)
def get_method_version( def get_method_version(
self, self,
method_id: str, method_id: str,
+64
View File
@@ -42,6 +42,37 @@ class MethodService:
return method_id return method_id
def update_method(
self,
method_id: str,
name: str,
description: str | None = None,
) -> None:
if not method_id:
raise MethodServiceError(
"Method ID must not be empty"
)
if not name or not name.strip():
raise MethodServiceError(
"Method name must not be empty"
)
if self._methods.get_method(method_id) is None:
raise MethodServiceError(
"Method not found"
)
self._methods.update_method(
method_id=method_id,
name=name.strip(),
description=(
description.strip()
if description
else None
),
)
def create_method_version( def create_method_version(
self, self,
method_id: str, method_id: str,
@@ -64,6 +95,39 @@ class MethodService:
definition=definition, definition=definition,
) )
def update_method_version(
self,
method_id: str,
version: int,
definition: dict[str, Any],
) -> None:
if version <= 0:
raise MethodServiceError(
"Method version must be greater than zero"
)
try:
validate_method_definition(definition)
except MethodDefinitionError as exc:
raise MethodServiceError(str(exc)) from exc
if (
self._methods.get_method_version(
method_id,
version,
)
is None
):
raise MethodServiceError(
"Method version not found"
)
self._methods.update_method_version(
method_id=method_id,
version=version,
definition=definition,
)
def create_next_method_version( def create_next_method_version(
self, self,
method_id: str, method_id: str,
+100
View File
@@ -128,6 +128,106 @@ def methods_page():
methods=method_rows, methods=method_rows,
) )
@cracklab_bp.route(
"/methods/<method_id>/edit/",
methods=["GET", "POST"],
)
def method_edit_page(method_id: str):
error = None
conn = connect_database()
try:
service = MethodService(conn)
method = service.get_method(method_id)
if method is None:
raise MethodServiceError(
"Метод не найден."
)
latest = service.get_latest_method_version(
method_id,
)
if latest is None:
raise MethodServiceError(
"У метода отсутствует версия."
)
if request.method == "POST":
try:
name = request.form.get(
"name",
"",
).strip()
description = request.form.get(
"description",
"",
).strip()
hash_mode_raw = request.form.get(
"hash_mode",
"22000",
).strip()
if not name:
raise MethodServiceError(
"Введите название метода."
)
if hash_mode_raw != "22000":
raise MethodServiceError(
"Сейчас поддерживается только hash mode 22000."
)
steps = _build_steps_from_form()
definition = {
"schema_version": 1,
"hash_mode": 22000,
"steps": steps,
}
with conn:
service.update_method(
method_id=method_id,
name=name,
description=description or None,
)
service.update_method_version(
method_id=method_id,
version=latest["version"],
definition=definition,
)
return redirect(
url_for(
"cracklab.methods_page",
)
)
except (MethodServiceError, ValueError) as exc:
error = str(exc)
definition = json.loads(
latest["definition_json"]
)
return render_template(
"method_create.html",
active_page="methods",
error=error,
method=method,
definition=definition,
edit_mode=True,
)
finally:
conn.close()
@cracklab_bp.route("/methods/create/", methods=["GET", "POST"]) @cracklab_bp.route("/methods/create/", methods=["GET", "POST"])
def method_create_page(): def method_create_page():
@@ -322,11 +322,19 @@
<div> <div>
<h1 class="page-title"> <h1 class="page-title">
{% if edit_mode %}
Редактирование метода
{% else %}
Создание метода Создание метода
{% endif %}
</h1> </h1>
<div class="page-description"> <div class="page-description">
{% if edit_mode %}
Изменения будут сохранены в текущей версии метода.
{% else %}
Новый рабочий метод будет сохранён в базе данных. Новый рабочий метод будет сохранён в базе данных.
{% endif %}
</div> </div>
</div> </div>
@@ -366,6 +374,7 @@
type="text" type="text"
required required
autocomplete="off" autocomplete="off"
value="{{ method.name if method else '' }}"
> >
</div> </div>
@@ -405,7 +414,7 @@
id="description" id="description"
name="description" name="description"
placeholder="Например: имя + четыре цифры" placeholder="Например: имя + четыре цифры"
></textarea> >{{ method.description if method and method.description else '' }}</textarea>
</div> </div>
@@ -498,7 +507,11 @@
type="submit" type="submit"
class="methods-button" class="methods-button"
> >
{% if edit_mode %}
Сохранить изменения
{% else %}
Сохранить метод Сохранить метод
{% endif %}
</button> </button>
<a <a
@@ -516,6 +529,12 @@
<script> <script>
const editMode =
{{ "true" if edit_mode else "false" }};
const initialSteps =
{{ definition.get("steps", []) | tojson if definition else "[]" }};
const stepsContainer = const stepsContainer =
document.getElementById("steps"); document.getElementById("steps");
@@ -567,7 +586,16 @@
name, name,
label, label,
help, help,
value = "",
) { ) {
const displayValue =
value
? value.replace(
/^wordlist:/,
"",
)
: "";
return ` return `
<div class="method-field"> <div class="method-field">
<label> <label>
@@ -580,6 +608,7 @@
placeholder="insidepro-wpa" placeholder="insidepro-wpa"
required required
autocomplete="off" autocomplete="off"
value="${escapeHtml(displayValue)}"
> >
<div class="method-help"> <div class="method-help">
@@ -590,7 +619,10 @@
} }
function maskField(name) { function maskField(
name,
value = "",
) {
return ` return `
<div class="method-field"> <div class="method-field">
<label> <label>
@@ -603,6 +635,7 @@
placeholder="?d?d?d?d?d?d?d?d" placeholder="?d?d?d?d?d?d?d?d"
required required
autocomplete="off" autocomplete="off"
value="${escapeHtml(value)}"
> >
<div class="method-help"> <div class="method-help">
@@ -1004,6 +1037,7 @@
function renderStepFields( function renderStepFields(
stepNumber, stepNumber,
mode, mode,
stepData = null,
) { ) {
const prefix = const prefix =
`step_${stepNumber}`; `step_${stepNumber}`;
@@ -1016,6 +1050,7 @@
`${prefix}_dictionary`, `${prefix}_dictionary`,
"Словарь", "Словарь",
"Укажите логическое имя wordlist, которое настроено на клиенте.", "Укажите логическое имя wordlist, которое настроено на клиенте.",
stepData?.dictionary?.resource || "",
); );
} }
@@ -1025,12 +1060,14 @@
`${prefix}_dictionary_1`, `${prefix}_dictionary_1`,
"Словарь — Left", "Словарь — Left",
"Этот словарь образует левую часть кандидата.", "Этот словарь образует левую часть кандидата.",
stepData?.dictionaries?.[0]?.resource || "",
); );
html += dictionaryField( html += dictionaryField(
`${prefix}_dictionary_2`, `${prefix}_dictionary_2`,
"Словарь — Right", "Словарь — Right",
"Этот словарь образует правую часть кандидата.", "Этот словарь образует правую часть кандидата.",
stepData?.dictionaries?.[1]?.resource || "",
); );
} }
@@ -1038,6 +1075,7 @@
if (mode === "3") { if (mode === "3") {
html += maskField( html += maskField(
`${prefix}_mask`, `${prefix}_mask`,
stepData?.mask || "",
); );
html += ` html += `
@@ -1058,10 +1096,12 @@
`${prefix}_dictionary`, `${prefix}_dictionary`,
"Словарь — Left", "Словарь — Left",
"Словарь образует левую часть кандидата.", "Словарь образует левую часть кандидата.",
stepData?.dictionary?.resource || "",
); );
html += maskField( html += maskField(
`${prefix}_mask`, `${prefix}_mask`,
stepData?.mask || "",
); );
html += ` html += `
@@ -1079,6 +1119,7 @@
if (mode === "7") { if (mode === "7") {
html += maskField( html += maskField(
`${prefix}_mask`, `${prefix}_mask`,
stepData?.mask || "",
); );
html += ` html += `
@@ -1095,6 +1136,7 @@
`${prefix}_dictionary`, `${prefix}_dictionary`,
"Словарь — Right", "Словарь — Right",
"Словарь образует правую часть кандидата.", "Словарь образует правую часть кандидата.",
stepData?.dictionary?.resource || "",
); );
} }
@@ -1304,7 +1346,9 @@
} }
function addStep() { function addStep(
initialStep = null,
) {
stepCount += 1; stepCount += 1;
const currentStepNumber = const currentStepNumber =
@@ -1440,6 +1484,27 @@
</div> </div>
`; `;
if (initialStep) {
modeSelect.value =
String(
initialStep.attack_mode,
);
workloadProfile =
step.querySelector(
"[data-workload-profile]",
);
if (
workloadProfile &&
initialStep.workload_profile
) {
workloadProfile.value =
String(
initialStep.workload_profile,
);
}
}
stepsContainer.appendChild( stepsContainer.appendChild(
step, step,
@@ -1462,7 +1527,9 @@
); );
function updateStep() { function updateStep(
initialData = null,
) {
const mode = const mode =
modeSelect.value; modeSelect.value;
@@ -1475,6 +1542,7 @@
renderStepFields( renderStepFields(
number, number,
mode, mode,
initialData,
); );
modeLabel.textContent = modeLabel.textContent =
@@ -1498,7 +1566,9 @@
modeSelect.addEventListener( modeSelect.addEventListener(
"change", "change",
updateStep, () => {
updateStep();
},
); );
@@ -1521,13 +1591,38 @@
); );
updateStep(); if (initialStep) {
modeSelect.value =
String(
initialStep.attack_mode,
);
const workloadProfile =
step.querySelector(
"[data-workload-profile]",
);
if (
workloadProfile &&
initialStep.workload_profile
) {
workloadProfile.value =
String(
initialStep.workload_profile,
);
}
}
updateStep(
initialStep,
);
stepCountInput.value = stepCountInput.value =
stepCount; stepCount;
return step; return step;
}
function rebuildStepNumbers() { function rebuildStepNumbers() {
@@ -1693,8 +1788,15 @@
}, },
); );
if (initialSteps.length > 0) {
initialSteps.forEach(
(step) => {
addStep(step);
},
);
} else {
addStep(); addStep();
}
</script> </script>
{% endblock %} {% endblock %}
@@ -93,6 +93,13 @@
</div> </div>
<a
class="methods-button-secondary"
href="{{ url_for('cracklab.method_edit_page', method_id=method.id) }}"
>
Редактировать
</a>
</article> </article>
{% endfor %} {% endfor %}