Skip to content

enh(backup): harden file type and name constraints on restore#3341

Merged
MichaIng merged 7 commits into
devfrom
enh/restore-only-regular-files
May 24, 2026
Merged

enh(backup): harden file type and name constraints on restore#3341
MichaIng merged 7 commits into
devfrom
enh/restore-only-regular-files

Conversation

@MichaIng
Copy link
Copy Markdown
Member

Once we support Python 3.12+ only, we can use filter='data' instead, or additionally, which refuses to extract any kind of link, normalizes paths, skips owner/mode, and more more. With our hardcoded strict filename pattern and the isfile() check, however, all security concerns are addressed.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Tightens backup restore behavior in motioneye.config.restore() by restricting extraction to regular files during tarball restore, aiming to reduce risk from non-file tar members.

Changes:

  • Filter tar members using TarInfo.isfile() before applying the allowed filename patterns.
  • Keep existing pattern-based allowlist for config-related filenames during restore.

Comment thread motioneye/config.py Outdated
Comment thread motioneye/config.py Outdated
@MichaIng MichaIng force-pushed the enh/restore-only-regular-files branch from 4d7449a to 1bb928e Compare May 24, 2026 13:36
Once we support Python 3.12+ only, we can use `filter='data'` , which refuses to extract any kind of link, normalizes paths, skips paths pointing outside of the target dir, skips owner/mode, and more more. The `isfile()` check, however prevents extraction of any unintended file types, and the name pattern matching is hereby hardened to really only allow leading `./` segments, and no other combination of these characters, and no `/` anywhere else in the filename. This is needed since `fnmatchcase()` wildcard `*` matches `/` as well.

Signed-off-by: MichaIng <micha@dietpi.com>
@MichaIng MichaIng force-pushed the enh/restore-only-regular-files branch from 1bb928e to 47a40f5 Compare May 24, 2026 13:42
@MichaIng MichaIng changed the title enh(backup): restore only regular files enh(backup): harden file type and name constraints on restore May 24, 2026
@MichaIng
Copy link
Copy Markdown
Member Author

MichaIng commented May 24, 2026

@copilot
Does 27a80e7 really add anything in terms of security?

  • In tarfile.member.name, path elements are always represented in UNIX style as /. \ would be a literal character in the file basename.
  • If a member is not readable, extractall() would fail, but not cause a security issue.
  • As we skip on members with / (path elements) in their name, other than leading ./ segments, I see no way how anything can be extracted to anything other than os.path.join(settings.CONF_PATH, name) anyway.

Hence the loop added to extract every file individually seems to not actually add anything to security, but adds quite a lot of unnecessary processing. If so, we better keep it as it was and use # nosec B202 for now.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 24, 2026

@copilot
Does 27a80e7 really add anything in terms of security?

  • In tarfile.member.name, path elements are always represented in UNIX style ...

Agreed, and adjusted in e02b162. I reverted the per-file extraction loop and switched back to tf.extractall(settings.CONF_PATH, members=members) # nosec B202, while keeping the expanded regression tests for non-regular members, absolute paths, and path-element wildcard abuse.

Comment thread tests/test_config.py Outdated
@MichaIng
Copy link
Copy Markdown
Member Author

@copilot
Please apply the same logic applied with e369bb4 for backup/tarball member tests: assert that the full list of tarball members matches the list of expected members, instead of asserting for individual names whether they have or have not been added.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 24, 2026

@copilot
Please apply the same logic applied with e369bb4 for backup/tarball member tests: assert that the full list of tarbal...

Done in a621b98. Added _assert_tarball_members helper and replaced all scattered assertIn/assertNotIn checks in TestBackup with a single exact-list assertion per test.

@MichaIng MichaIng force-pushed the enh/restore-only-regular-files branch from 83481d2 to 4df84a0 Compare May 24, 2026 16:02
We do not test the actual restored file or tarball member contents, hence skip this entirely to avoid confusion or false implications. Create and check multiple files and tarball members with positional arguments in a single function call, instead of multple calls, a list or dict. Move `assertIsNotNone(data)` from backup tests into the members assert function. The same for checking the restore result is not such a coding benefit, since its assert function does not currently take or need the JSON result. Instead, it is sufficient to check the expected result in a single separate test, and skip it entirely in all others, where it is about the restored files, not about the return value of the restore function. A test with `settings.ENABLE_REBOOT = True` => `{reboot: True}` could make sense though.

Signed-off-by: MichaIng <micha@dietpi.com>
@MichaIng MichaIng force-pushed the enh/restore-only-regular-files branch from 4df84a0 to 437aaeb Compare May 24, 2026 16:20
@MichaIng MichaIng merged commit 2b9d7e2 into dev May 24, 2026
20 checks passed
@MichaIng MichaIng deleted the enh/restore-only-regular-files branch May 24, 2026 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

3 participants