{"record":{"id":"eec1c4942f085426","repo":"jdx/mise","slug":"the-release-list-of-packslip-project-has-sequenc","errorCode":null,"errorMessage":"the release list of packslip:{project} has sequence {sequence}, but sequence {last} was already accepted; refusing to go back","messagePattern":"the release list of packslip:(.+?) has sequence (.+?), but sequence (.+?) was already accepted; refusing to go back","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/packslip_pins.rs","lineNumber":213,"sourceCode":"        pins.pins.insert(project.to_string(), updated.clone());\n        save(path, &pins)?;\n    }\n    Ok(updated)\n}\n\n/// Refuse a release list whose sequence is below one already accepted for\n/// the project, and remember the highest seen.\npub(crate) fn check_sequence(project: &str, sequence: u64) -> Result<()> {\n    check_sequence_at(&pins_file(), project, sequence)\n}\n\npub(crate) fn check_sequence_at(path: &Path, project: &str, sequence: u64) -> Result<()> {\n    let _lock = locked(path)?;\n    let mut pins = load(path)?;\n    if let Some(last) = pins.sequences.get(project).copied()\n        && sequence < last\n    {\n        bail!(\n            \"the release list of packslip:{project} has sequence {sequence}, but sequence {last} was already accepted; refusing to go back\"\n        );\n    }\n    if pins.sequences.get(project) != Some(&sequence) {\n        pins.sequences.insert(project.to_string(), sequence);\n        save(path, &pins)?;\n    }\n    Ok(())\n}\n\n/// An absent supplementary list is allowed only before this machine has\n/// accepted one. Its disappearance must not undo signed withdrawals.\npub(crate) fn check_missing_list(project: &str) -> Result<()> {\n    check_missing_list_at(&pins_file(), project)\n}\n\nfn check_missing_list_at(path: &Path, project: &str) -> Result<()> {\n    let _lock = locked(path)?;","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/packslip_pins.rs#L195-L231","documentation":"The packslip pins file stores a monotonic sequence number per project representing the release list already accepted. `check_sequence_at` refuses any release list with a sequence lower than the recorded one, preventing rollback attacks where an attacker serves an older (possibly vulnerable) release list. Once a sequence is accepted it cannot go back until the pin is forgotten.","triggerScenarios":"Installing/updating a packslip project when the fetched release list's sequence number is less than the highest previously accepted sequence in the pins file — e.g. a mirror serving stale data, a CDN caching an old manifest, or a vendored downgrade.","commonSituations":"Stale CDN/mirror cache after a vendor re-publish; point-in-time snapshot or proxy serving an older manifest; switching to a frozen vendor feed; restored backup of the pins file alongside a newer upstream (reverse case) or older remote list.","solutions":["Wait for or force-refresh the upstream release list (bust cache, use the official endpoint) so it serves the current sequence","Check for a proxy/mirror serving stale manifests and bypass it (unset MISE proxy vars, use direct network)","Verify the vendor didn't actually roll back their release feed; if genuine and announced, run `mise packslip forget <project>` and reinstall to reset the sequence","Inspect the pins file location for the recorded sequence to confirm which side is stale"],"exampleFix":"// before: stale mirror serves sequence 12 after 13 accepted\nmise install packslip:example-cli   # refusing to go back\n// after: fetch current list / reset pin if vendor confirmed rollback\nmise packslip forget example-cli && mise install packslip:example-cli","handlingStrategy":"retry","validationCode":"mise packslip status <project>  # compare remote release-list sequence with accepted pin","typeGuard":null,"tryCatchPattern":"match check_sequence_at(pins_path, project, seq) {\n    Err(e) if is_stale_cache_error(&e) => { bust_cache(); retry_with_fresh_manifest(); }\n    Err(e) => return Err(e), // genuine rollback: refuse\n    Ok(()) => proceed(),\n}","preventionTips":["Avoid caching proxies/CDNs for packslip release lists, or configure short TTLs","Always fetch the release list from the official endpoint","If a downgrade is genuinely required (vendor rollback), do `mise packslip forget <project>` explicitly rather than deleting pin files by hand"],"tags":["security","supply-chain","packslip","rollback","cache"],"backgroundTag":"invalid-state-transition","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}