ankitects / ankitects/anki

Case-sensitivity for field names appears to be inconsistent

Open
#2,186 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
30.5k
Forks
3.2k
Avg merge
2d 8h
Merged PRs (30d)
72

Description

Got this error while fuzzing. From tracing through the rust code, it looks like it's implicitly adding a `+` character to deduplicate field names. This can be confirmed by trying this example in the GUI. A `+` is appended to both the field name and its use in the template.

So the field deduplication is case-insensitive, which is in direct conflict with how field replacements work (they are case sensitive). Moreover the [documentation](https://docs.ankiweb.net/templates/fields.html) says fields are case sensitive. This is pretty confusing.

Somewhat unrelated, but I saw https://github.com/ankitects/anki/issues/1979. Not that this is a common pain point, but for what it's worth, I am in favor of dropping custom collations from the DB as they make direct DB ops rather painful.

```python
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = , service = 4, method = 2
input = b'\n\x95\x06{"id":0,"name":"0-d3c25","type":0,"mod":0,"usn":0,"sortf":0,"did":null,"tmpls":[{"name":"0","ord":null,"qf...\\\\setlength{\\\\parindent}{0in}\\n\\\\begin{document}\\n","latexPost":"\\\\end{document}","latexsvg":false,"req":[]}'

def _run_command(self, service: int, method: int, input: bytes) -> bytes:
try:
return self._backend.command(service, method, input)
except Exception as error:
error_bytes = bytes(error.args[0])

err = backend_pb2.BackendError()
err.ParseFromString(error_bytes)
> raise backend_exception_to_pylib(err)
E anki.errors.CardTypeError: Card template ⁨1⁩ in notetype '⁨0-d3c25⁩' has a problem.
See the preview for more information.
E Falsifying example: test_add_notetype(
E data=data(...),
E )
E Draw 1 (add nt: name): '0'
E Draw 2 (add nt: fieldnames): ['A', 'a']
E Draw 3 (add nt: tnames): ['0']
E Draw 4 (add nt: qtxts): ['']
E Draw 5 (add nt: atxts): ['']
E Draw 6 (fmts: placeholders): {'{{A}}'}
E Draw 7 (fmts: frepl locs): [0]
E Draw 8 (fmts: placeholders): {'{{a}}'}
E Draw 9 (fmts: frepl locs): [0]

../../conda/envs/anki/lib/python3.9/site-packages/anki/_backend/__init__.py:146: CardTypeError
==================================================== short test summary info =====================================================
FAILED tests/test_invalid_field_names.py::test_add_notetype - anki.errors.CardTypeError: Card template ⁨1⁩ in notetype '⁨0-d3c2...
======================================================= 1 failed in 16.41s =======================================================
```

```rust
impl Notetype {
pub(crate) fn ensure_names_unique(&mut self) {
let mut names = HashSet::new();
for t in &mut self.templates {
loop {
let name = UniCase::new(t.name.clone());
if !names.contains(&name) {
names.insert(name);
break;
}
t.name.push('+');
}
}
names.clear();
for t in &mut self.fields {
loop {
let name = UniCase::new(t.name.clone());
if !names.contains(&name) {
names.insert(name);
break;
}
t.name.push('+');
}
}
}
```

Contributor guide

Open the contributing guide

Research direction

Start with Notetype::ensure_names_unique in the Rust backend and the failing test in tests/test_invalid_field_names.py. Trace how field names are deduplicated and then resolved in templates, using the A/a reproducer from the issue. Done means the documented case-sensitivity behavior is consistent and the regression test passes.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, rust
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.