Skip to content

Crackme obfuscation tags: display, search, submission & review (#17, #162)#163

Open
xusheng6 wants to merge 19 commits into
mainfrom
feature/crackme-tags-17
Open

Crackme obfuscation tags: display, search, submission & review (#17, #162)#163
xusheng6 wants to merge 19 commits into
mainfrom
feature/crackme-tags-17

Conversation

@xusheng6

Copy link
Copy Markdown
Contributor

Implements crackme obfuscation tags (closes #17) and makes tag/search results bookmarkable (closes #162).

What this adds

  • Tags on crackmes — a controlled vocabulary of obfuscation classes (Anti-debugging, Packer, String/data encryption, …) with nested sub-labels (specific anti-debug methods, packers, ciphers, control-flow & anti-disasm techniques), seeded from the AI-labeled crackmes-RE dataset.
  • Display — green tag chips on the crackme page; a "?" reveals a help note explaining the tags are AI-generated (from write-ups/comments) and may be inaccurate, with a link to the dataset.
  • Submission — authors pick tags via grouped checkboxes (ticking a technique auto-ticks its category); tags are optional.
  • Search — filter by any tag; results are driven by URL query params so they're bookmarkable/shareable (/search?tags=UPX). Tag chips link straight to a search.
  • Reviewers — can override tags when reviewing a pending crackme and when editing an approved one.
  • Tag change requests — any logged-in user can request adds/removes from a crackme page; reviewers get a queue to approve (applies the change) or reject, notifying the requester.

Vocabulary is data, not code

The controlled vocabulary lives in a tag_vocabulary MongoDB collection so it can be updated without code changes when the dataset changes:

  • script/sync_tag_vocabulary.py rebuilds the vocabulary document from the dataset.
  • script/import_tags.py (re)applies crackme tags.
  • The app loads it once per process (cached), falling back to a built-in default when the collection is empty (fresh DB / tests). Update flow: sync → import → restart workers.

Notable UX details

  • Grouped-checkbox layout shared across upload / request / reviewer pages (category ↔ technique auto-linking); search intentionally keeps independent checkboxes.
  • Tag change dialog resets to the crackme's current tags on open (no browser-cached draft) and has Undo/Reset; the reviewer edit form likewise resets to saved values on load/bfcache.

Tests

227 passing, including new coverage for the vocabulary (default fallback + DB override), submission, GET-based tag search, tag change requests, and reviewer overrides.

🤖 Generated with Claude Code

xusheng6 and others added 19 commits July 13, 2026 21:43
Introduces a controlled vocabulary of 21 anti-analysis / obfuscation tags
(anti-debugging, string encryption, packers, ...) seeded from the AI-labeled
crackmes-RE dataset, and wires them through the whole crackme lifecycle.

- app/services/tags.py: 21-class vocabulary, normalize_tags(), dataset URL
- Crackmes carry a `tags` field: authors pick tags at submission, tags show on
  the crackme page (each links to a tag search) with a "?" explaining the tags
  are AI-generated from write-ups/comments and may be inaccurate
- Search: filter by tags (matches crackmes carrying all selected tags)
- Reviewers can override tags when reviewing a pending crackme and when editing
  an approved one
- Tag change requests: any logged-in user can request tag adds/removes; a new
  reviewer queue (dashboard link + count) approves (applies) or rejects them,
  notifying the requester
- FAQ #tags entry; script/import_tags.py to backfill tags from the dataset
- Tests for vocabulary, model, submission, search, requests, reviewer overrides

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends the tag vocabulary with the dataset's finer sub-labels: specific
anti-debug methods (IsDebuggerPresent, ptrace, ...), packer names (UPX, FSG,
...), and control-flow techniques (CFF, ...), nested under their parent class.

- app/services/tags.py: SUBLABELS, TAG_GROUPS (grouped view), ALL_TAGS (flat
  canonical order: each class followed by its sub-labels); normalize_tags now
  validates and orders across classes + sub-labels
- Submission, request modal, and reviewer forms render sub-labels indented
  under their class; search uses <optgroup> so a category ("any") or a specific
  technique can be selected
- import_tags.py also ingests antidebug_methods / packers / controlflow_methods
- Tests for sub-label vocabulary, ordering, grouped structure, submission, and
  search-by-sub-label

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Tag chips rendered one-per-line because each chip was followed by a hidden
  <form> inside a <p>; a <form> start tag auto-closes an open <p>, breaking the
  inline flow. Use one shared hidden search form and a <div> container so chips
  sit inline.
- The "AI-generated, may be inaccurate" note is no longer always shown; it is
  revealed by clicking the "?" and now clarifies that new crackmes are tagged by
  their authors while only older imported tags are AI-generated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Search now runs off URL query parameters so results can be bookmarked and
shared (issue #162):

- /search accepts GET: a bare /search shows the empty form, any parameter
  (e.g. /search?tags=Packer) runs the search. POST is kept for backward
  compatibility; both share one _render_search(source) helper.
- The search form and all pagination controls submit via GET (CSRF hidden
  inputs dropped from these read-only forms so URLs stay clean).
- Tag chips on a crackme page are now plain GET links to
  /search?tags=<url-encoded tag>, so clicking one produces a bookmarkable URL
  and the tag stays selected in the form on arrival.
- Tests for GET-based tag search, the empty state, and chip link encoding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Tag change requests now use a two-list transfer UI: left = not applied,
  right = applied; click a tag to move it. Moving a technique also applies its
  category; removing a category removes its techniques. The form submits the
  full desired "applied" set and the server diffs it against the crackme's
  current tags to derive add/remove.
- On the submission form (and reviewer set-tags / edit forms), ticking a
  specific technique auto-ticks its parent category, and un-ticking a category
  clears its techniques, so tags never end up with an orphaned sub-label.
- Tags remain optional (a crackme with no matching technique may have none),
  but the submission text no longer calls them "optional" and asks authors to
  tag every technique that applies.
- Tests updated for the applied-set request semantics and the no-tags path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the click-to-move dual list with an explicit two-list transfer widget:
select one or more tags, then move them with the "=>" / "<=" buttons. Adds a
Reset (restore original set) and Undo (revert last move) button. Moving a
technique into "applied" pulls in its parent category; moving a category out
drops its techniques, so a sub-label is never applied without its parent.

- New static/js/tag_transfer.js (TagTransfer.init) + shared CSS in custom.css,
  used in both the tag-change request modal (submits the desired "applied" set)
  and the reviewer edit-crackme page (submits the full "tags" set). The reviewer
  edit tags editor moves from grouped checkboxes to this widget.
- Test updated: edit page now renders the widget seeded with current tags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Undo/Reset never worked because the buttons sat outside the element the
  widget scans (root.querySelector couldn't find them, so no listeners were
  attached). Wrap the two-list transfer and its action buttons in a single
  root container on both the request modal and the reviewer edit page.
- Enlarge the tag change dialog (modal-lg, wider container, taller lists,
  bigger click targets) so tags are easier to click.
- The "Request a tag change" link now lives inside the "?" help box, beneath
  "you can request a change", instead of always showing in the header.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- In the tag transfer widget, removing a technique now also removes its parent
  category when no other technique of that category remains applied (the
  inverse of "adding a technique adds its category").
- Also fix two NUL bytes that had crept into the Reset comparison line (which
  made the JS file classify as binary); the comparison now uses JSON.stringify
  and the file is clean ASCII. Replace the raw arrow glyph with ↳ for the
  same reason.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In the tags help box, logged-out users now see a "Log in to request a tag
change" link instead of a dead-end mention, matching the logged-in link.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The dataset now labels named commercial protectors under the packers field
(VMProtect, ASProtect, .NET Reactor, ConfuserEx, Enigma, Themida, ExeCryptor,
WinLicense, SmartAssembly, PELock, CodeVirtualizer, Dotfuscator) and adds an
"Anti-attach / thread suspension" anti-debug method. Add these to the
controlled vocabulary (ordered by frequency) so the reimport keeps them
instead of dropping them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…el sets) (#17)

The dataset taxonomy was updated: obfuscation classes dropped from 21 to 16
(Timing checks, Exception-based, Commercial protector, Stripped/no symbols and
Anti-attach/thread tricks were folded into other classes), and three new
sub-label families were added:
- antidisasm_methods  -> Anti-disassembly
- crypto_methods      -> Crypto / hash algorithm
- encryption_methods  -> String / data encryption

AES / Base64 / RC4 / TEA-XTEA appear under both crypto and encryption, so they
are qualified per source ("AES (crypto)" vs "AES (encryption)") to keep each
tag under exactly one parent (dataset_sublabel_tag). The import script now
ingests all six sub-label fields with this qualification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The controlled vocabulary (obfuscation classes + sub-labels + field mapping +
qualification rules) now lives in a `tag_vocabulary` MongoDB collection (single
document, _id="current"), so it can be updated when the dataset changes without
editing code.

- app/services/tags.py: loads the active vocabulary from the DB (cached),
  falling back to the built-in DEFAULT_* baseline when the collection is empty
  (fresh DB / tests / DB down). Exposes accessor functions (get_tag_groups,
  get_classes, normalize_tags, dataset_sublabel_tag, get_sublabel_fields,
  get_dataset_url, reload_vocabulary) instead of module constants.
- script/sync_tag_vocabulary.py: rebuilds the vocabulary document from the
  dataset (classes/sub-labels ordered by frequency, shared cipher names
  auto-detected and qualified) or seeds the built-in default.
- Controllers, reviewer routes, and import_tags.py use the accessors; the tag
  vocabulary cache is reset between tests.
- Tests for the default fallback and DB override.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#17)

Replace the two-list transfer widget (tag change request modal + reviewer edit
page) and the search page's multi-select dropdown with the same grouped
checkbox layout used on the upload form: each class is a bold checkbox with its
sub-labels indented underneath.

- New shared partial templates/partial/tags_checkboxes.html (parametrized by
  input name + currently-checked tags) and static/js/tag_checkboxes.js
  (sub->parent auto-check), reused by upload, request modal, reviewer view/edit,
  and search.
- Retire static/js/tag_transfer.js and its CSS.
- Search uses checkboxes (in a scroll box); still GET/bookmarkable, still
  matches crackmes carrying all checked tags.
- Tests updated for the checkbox markup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The modal-body scrolled as a whole (Spectre caps it at 50vh), so the optional
reason box and Submit button could scroll out of view. Now only the checkbox
list scrolls (.tags-scroll, max 50vh) while the reason field and submit stay
pinned below it, with breathing room beneath the button. The dialog is also
larger (900px / 94vw).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the shared tag checkbox helper so that, in addition to selecting a
category when a technique is ticked and clearing techniques when a category is
un-ticked, un-ticking the last remaining technique of a category now also
un-ticks the category. Applies to the upload, request, and reviewer view/edit
forms. Search intentionally keeps independent checkboxes (no auto-linking).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Browsers persist user-modified checkbox state across reloads, so reopening the
"Request a tag change" dialog could show stale ticks on top of the crackme's
actual tags. The dialog now resets to the crackme's current tags every time it
opens (no draft kept between opens) and the form is marked autocomplete="off".

Also add Undo (step back through changes) and Reset (back to the current tags)
buttons to the dialog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Like the tag request dialog, the reviewer Edit crackme form could show
browser-restored (stale) field/checkbox state on reload or back-forward. Reset
the form to the crackme's saved values on pageshow (load + bfcache) and mark it
autocomplete="off".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace sync_tag_vocabulary.py + import_tags.py with a single script that runs
both phases in the required order (vocabulary, then crackme tags), removing the
footgun where re-tagging with a stale vocabulary silently dropped newly added
tags. Flags: --vocab-only, --tags-only, --seed-default. The tags phase is now
authoritative (clears then sets from the dataset) so crackmes dropped from the
dataset also lose their tags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Search page should use GET so that it can be bookmarked Support adding tags to crackmes

1 participant