Skip to content

Add test for CVE-2026-35535: Privilege escalation#27

Merged
jakub-vavra-cz merged 1 commit into
RedHat-SP-Security:masterfrom
jakub-vavra-cz:drop_mailer
Apr 23, 2026
Merged

Add test for CVE-2026-35535: Privilege escalation#27
jakub-vavra-cz merged 1 commit into
RedHat-SP-Security:masterfrom
jakub-vavra-cz:drop_mailer

Conversation

@jakub-vavra-cz
Copy link
Copy Markdown
Collaborator

@jakub-vavra-cz jakub-vavra-cz commented Apr 20, 2026

Summary by Sourcery

Tests:

  • Introduce a critical BareClient sudo test that uses a fake mailer to assert the mailer process runs with the non-root UID/GID of the invoking user instead of root, preventing privilege escalation.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 20, 2026

Reviewer's Guide

Adds a new critical pytest integration test that reproduces and guards against CVE-2026-35535 by verifying that sudo’s mailer runs under the invoking user’s UID/GID rather than root when mail_always is enabled.

File-Level Changes

Change Details Files
Add end-to-end sudo mailer privilege-escalation regression test for CVE-2026-35535.
  • Introduce a bash fakemailer script string that records effective UID/GID to /tmp/mail..
  • Add a BareClient pytest test that provisions a fixed-UID local user, installs the fakemailer, and configures sudoers with mailerpath, mail_always, and a PASSWD rule for /usr/bin/whoami.
  • Validate baseline behavior by running fakemailer directly as the test user and asserting recorded UID/GID match expected values.
  • Execute sudo /usr/bin/whoami as the test user, then assert the resulting fakemailer log does not show root UID/GID and instead matches the test user’s IDs, failing with CVE-specific messages if not.
  • Use visudo to syntax-check the sudoers drop-in and manage temporary /tmp/mail.* files around the test run.
pytest/tests/test_security.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The test description says the user should have UID/GID (10001/20001), but the code uses expected_gid = 10001 and never explicitly sets the user's primary GID; consider either updating the description or setting gid to the intended value and adjusting expected_gid accordingly to avoid confusion and brittle expectations.
  • Relying on time.sleep(3) before reading /tmp/mail.* makes the test timing-dependent; consider polling for the mail file with a timeout instead to make the test more robust on slower or more variable environments.
  • The error messages around reading /tmp/mail.* mention ls even though cat is used; updating these to reflect the actual command should make failures easier to interpret.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The test description says the user should have UID/GID (10001/20001), but the code uses `expected_gid = 10001` and never explicitly sets the user's primary GID; consider either updating the description or setting `gid` to the intended value and adjusting `expected_gid` accordingly to avoid confusion and brittle expectations.
- Relying on `time.sleep(3)` before reading `/tmp/mail.*` makes the test timing-dependent; consider polling for the mail file with a timeout instead to make the test more robust on slower or more variable environments.
- The error messages around reading `/tmp/mail.*` mention `ls` even though `cat` is used; updating these to reflect the actual command should make failures easier to interpret.

## Individual Comments

### Comment 1
<location path="pytest/tests/test_security.py" line_range="50-51" />
<code_context>
+    :customerscenario: False
+    """
+    username = "testuser"
+    expected_uid = 10001
+    expected_gid = 10001
+    mailer_drop_in_path = "/etc/sudoers.d/00-cve-35535-mailer"
+    client.user(username).add(uid=expected_uid, password="Secret123")
</code_context>
<issue_to_address>
**issue:** Align expected GID with the documented test setup to avoid a misleading assertion

The docstring states the user should have UID/GID 10001/20001, but the test uses 10001 for both. This makes the test scenario inconsistent with its description and could hide issues if the real primary GID differs. Please either set `expected_gid` to the intended primary GID (e.g. 20001 and pass it to `client.user(...).add`) or update the docstring so it matches the actual UID/GID used.
</issue_to_address>

### Comment 2
<location path="pytest/tests/test_security.py" line_range="98-104" />
<code_context>
+        f"sudo must allow {username!r} to run /usr/bin/whoami with password Secret123 (sudoers rule from "
+        f"this test); otherwise the mailer is never invoked and the CVE check cannot run."
+    )
+    time.sleep(3)
+    after = client.host.conn.run("cat /tmp/mail.* 2>/dev/null")
+    client.host.conn.run("rm -f /tmp/mail.*", raise_on_error=False)
+    assert after.rc == 0 and after.stdout.strip(), (
</code_context>
<issue_to_address>
**suggestion (testing):** Avoid fixed sleep; wait explicitly for the mail file to appear to reduce flakiness

The hard-coded `time.sleep(3)` risks flakiness on slower systems and wastes time on faster ones. Instead, poll for `/tmp/mail.*` for a bounded period (e.g., loop up to N seconds with a short sleep) and break as soon as `cat /tmp/mail.*` returns non-empty output, still failing if the mailer never runs.

```suggestion
    assert client.auth.sudo.run(username, "Secret123", command="/usr/bin/whoami"), (
        f"sudo must allow {username!r} to run /usr/bin/whoami with password Secret123 (sudoers rule from "
        f"this test); otherwise the mailer is never invoked and the CVE check cannot run."
    )

    # Poll for fakemailer output instead of using a fixed sleep to reduce flakiness
    timeout_seconds = 10
    poll_interval = 0.5
    deadline = time.time() + timeout_seconds
    after = None

    while time.time() < deadline:
        after = client.host.conn.run("cat /tmp/mail.* 2>/dev/null")
        if after.rc == 0 and after.stdout.strip():
            break
        time.sleep(poll_interval)

    if after is None:
        # Fallback in the unlikely event the loop body never ran
        after = client.host.conn.run("cat /tmp/mail.* 2>/dev/null")

    client.host.conn.run("rm -f /tmp/mail.*", raise_on_error=False)
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread pytest/tests/test_security.py
Comment thread pytest/tests/test_security.py
Copy link
Copy Markdown
Collaborator

@shridhargadekar shridhargadekar left a comment

Choose a reason for hiding this comment

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

Looks good to me

@jakub-vavra-cz jakub-vavra-cz merged commit 331762c into RedHat-SP-Security:master Apr 23, 2026
3 of 7 checks passed
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.

2 participants