Europass: find out what the platform exports today, and read it #244

Open
opened 2026-09-16 11:59:23 +00:00 by tiagoagueda · 0 comments
Owner

Split out of #235, whose last bullet was a research task rather than a fix. #235 did the
three concrete things (levels left unset, record_language from the file's locale,
translated group labels) and deliberately left this one alone, because half-building a
reader against a format nobody here has seen is worse than not having one.

What we actually know

  • reader.read sniffs on the first non-whitespace byte: < is XML, { is JSON, anything
    else is refused with a message naming the two formats.
  • read_xml refuses anything with no LearnerInfo element; read_json accepts
    SkillsPassport.LearnerInfo or a bare LearnerInfo.
  • Both fixtures in tests/data/ are SkillsPassport. Neither came from europass.europa.eu
    this year.
  • A PDF is refused outright, because it starts with %.

What is plausible and unverified

The audit's claim is that today's Europass editor exports HR-Open Candidate XML, or a
PDF with the XML embedded, rather than SkillsPassport. Nobody has checked. If it is true,
"import your Europass CV" is a promise Postulo does not keep for anybody who made their CV
this year, and the refusal message tells them their own export is not a Europass file.

What to do first, before writing any code

Make a Europass CV on europass.europa.eu and download it in every format the Download
menu offers. Commit each as a fixture with the personal details replaced, the way
tests/data/europass.xml already is, and say in the file's comment what produced it and
when — that comment is the thing that stops the next person guessing again.

Then, depending on what came back

  • Still SkillsPassport-shaped. Nothing to build. Say so in the fixture comment and on
    Your career record, and close this.
  • HR-Open Candidate. A third reader behind the same importing.Record, which is what
    the Record shape exists for (#129). It cannot sniff on the first byte, since it is XML
    too, so read_xml grows a look at the root element's local name and dispatches; the
    refusal message then has to name three formats rather than two.
  • A PDF carrying the XML. This is the one that needs a decision rather than a patch.
    An embedded file lives in /Names /EmbeddedFiles and reaching it means parsing PDF
    structure, which Postulo has no dependency for today (WeasyPrint writes and never reads).
    Adding one is a supply-chain decision, and it is an untrusted-input decision besides: the
    uploaded file is a stranger's, so whatever is added has to be held to the same rules as
    the rest of the importer kind — the size cap before parsing, no object-stream bomb, and
    refuse_unreadable on what comes out of the attachment as well as on what went in,
    because the payload is a second file.

Also worth settling while looking

Whether the editor's PDF has any machine-readable payload at all any more. If it does not,
the upload form should say which file to choose rather than leaving somebody to find out by
being refused; accept=".xml,.json,…" on the input already hints at it, and the sentence
above it does not.

Split out of #235, whose last bullet was a research task rather than a fix. #235 did the three concrete things (levels left unset, `record_language` from the file's locale, translated group labels) and deliberately left this one alone, because half-building a reader against a format nobody here has seen is worse than not having one. ## What we actually know - `reader.read` sniffs on the first non-whitespace byte: `<` is XML, `{` is JSON, anything else is refused with a message naming the two formats. - `read_xml` refuses anything with no `LearnerInfo` element; `read_json` accepts `SkillsPassport.LearnerInfo` or a bare `LearnerInfo`. - Both fixtures in `tests/data/` are SkillsPassport. Neither came from europass.europa.eu this year. - A PDF is refused outright, because it starts with `%`. ## What is plausible and unverified The audit's claim is that today's Europass editor exports HR-Open **Candidate** XML, or a PDF with the XML embedded, rather than SkillsPassport. Nobody has checked. If it is true, "import your Europass CV" is a promise Postulo does not keep for anybody who made their CV this year, and the refusal message tells them their own export is not a Europass file. ## What to do first, before writing any code Make a Europass CV on europass.europa.eu and download it in **every** format the Download menu offers. Commit each as a fixture with the personal details replaced, the way `tests/data/europass.xml` already is, and say in the file's comment what produced it and when — that comment is the thing that stops the next person guessing again. ## Then, depending on what came back - **Still SkillsPassport-shaped.** Nothing to build. Say so in the fixture comment and on *Your career record*, and close this. - **HR-Open Candidate.** A third reader behind the same `importing.Record`, which is what the `Record` shape exists for (#129). It cannot sniff on the first byte, since it is XML too, so `read_xml` grows a look at the root element's local name and dispatches; the refusal message then has to name three formats rather than two. - **A PDF carrying the XML.** This is the one that needs a decision rather than a patch. An embedded file lives in `/Names /EmbeddedFiles` and reaching it means parsing PDF structure, which Postulo has no dependency for today (WeasyPrint writes and never reads). Adding one is a supply-chain decision, and it is an untrusted-input decision besides: the uploaded file is a stranger's, so whatever is added has to be held to the same rules as the rest of the importer kind — the size cap before parsing, no object-stream bomb, and `refuse_unreadable` on what comes *out* of the attachment as well as on what went in, because the payload is a second file. ## Also worth settling while looking Whether the editor's PDF has any machine-readable payload at all any more. If it does not, the upload form should say which file to choose rather than leaving somebody to find out by being refused; `accept=".xml,.json,…"` on the input already hints at it, and the sentence above it does not.
tiagoagueda added this to the 0.4.0 milestone 2026-09-16 11:59:23 +00:00
tiagoagueda modified the milestone from 0.4.0 to 0.5.0 2026-09-19 10:31:12 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo#244
No description provided.