Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show more helpful messages for invalid passwords #815

Merged
merged 5 commits into from
Oct 10, 2021

Conversation

bhrutledge
Copy link
Contributor

@bhrutledge bhrutledge commented Sep 25, 2021

Update 10/5: Following the comments below, the implementation has changed to print warnings when it looks like the password isn't valid.

As noted in #671 (comment), when Windows users use Ctrl+V to paste their password, the resulting value is a control character, as documented in https://bugs.python.org/issue37426. The current behavior via #685 is to show <hidden> in that case, but that's not useful information. The intention of that PR was to show <empty>, which would have indicated that pasting didn't work as expected.

This is a lightweight attempt to give the user better feedback, and aid troubleshooting. In #685 (comment), @sigmavirus24 said:

I am strongly against revealing the length of a password. That's more metadata than is necessary.

However, given that this would be opt-in via --verbose, and the prevalence of this issue, I think the benefit outweighs the risk.

A further step could be to detect this case, and display a helpful warning message (possibly as a link to a new section in Twine's docs).

@bhrutledge
Copy link
Contributor Author

@sigmavirus24 Any thoughts about my rationale for adding this?

@sigmavirus24
Copy link
Member

I have thoughts but haven't been near a computer to respond. Presently mostly using my phone for triage of notifications these days. To jot them down

  • we're leaking information that's sensitive, especially of this is run somewhere like GitHub actions with the appropriate flag (probably less of a concern with tokens but still not good security posture)

  • we could detect "this is windows and that code point is what happens when someone fails to paste their password" at the first character and be helpful (tell the user they pasted incorrectly on a Windows prompt, link them to docs, and reprompt for the password)

  • we could print something more helpful for "empty" or detect this and log something more helpful there too

There are better solutions than printing the number of characters in the password

@sigmavirus24
Copy link
Member

Additionally I think verbose is repeatable which means we could have a trade off where after -vvv (or more) we show the asterisks.

@bhrutledge
Copy link
Contributor Author

@sigmavirus24 Thanks for your thoughts. I realize we're in bike-shedding territory here, but it's helpful for me to think through this sort of thing.

we're leaking information that's sensitive, especially of this is run somewhere like GitHub actions with the appropriate flag

I was thinking that --verbose in CI seems unlikely, but a GitHub search shows that's an incorrect assumption. 😄 Still, it feels like the risk of a compromised password is low (although I'm far from a security expert).

I think verbose is repeatable which means we could have a trade off where after -vvv (or more) we show the asterisks.

It's currently a boolean, and I'm not inclined to add the complexity of additional levels for this.

twine/twine/settings.py

Lines 149 to 155 in 4c9d8c1

@verbose.setter
def verbose(self, verbose: bool) -> None:
"""Initialize a logger based on the --verbose option."""
self._verbose = verbose
root_logger = logging.getLogger("twine")
root_logger.addHandler(logging.StreamHandler(sys.stdout))
root_logger.setLevel(logging.INFO if verbose else logging.WARNING)

we could detect "this is windows and that code point is what happens when someone fails to paste their password" (or)
we could print something more helpful for "empty" or detect this and log something more helpful (tell the user they pasted incorrectly on a Windows prompt, link them to docs, and reprompt for the password)

I was thinking about this, but I prefer a lightweight, general indication of "unusable password", rather than spending time writing and maintaining a complex edge case. I'm also wary of re-prompting for password, because Twine currently doesn't do that.

I might try to word-smith a general-purpose logger.warning message if the password is empty/short/unprintable. Beyond that, a quick survey of other CLI tools that prompt for a password (including Flit and Poetry) didn't turn up any that try to be more helpful. That doesn't mean we shouldn't try, but does make this a lower-priority issue for me.

@sigmavirus24
Copy link
Member

I was thinking that --verbose in CI seems unlikely, but a GitHub search shows that's an incorrect assumption. smile Still, it feels like the risk of a compromised password is low (although I'm far from a security expert).

The issue isn't "we'll accidentally leak the plain-text of the password". The issue is that we're not providing a potential malicious actor enough information about the password to cut the problem space of brute forcing/generating the possible passwords significantly.

Without that information, the problem space is much larger.

It's currently a boolean, and I'm not inclined to add the complexity of additional levels for this.

Having different levels of available information is quite useful. Flake8 leverages this and it helps users find the right level of information when debugging a problem and helps developers of the tool and plugins as well.

I was thinking about this, but I prefer a lightweight, general indication of "unusable password", rather than spending time writing and maintaining a complex edge case.

Sure, does PyPI have a "minimum length" requirement on passwords? If so len(password) < minimum_length exits early

I'm also wary of re-prompting for password, because Twine currently doesn't do that.

I'd like to understand what makes you wary of this.

Beyond that, a quick survey of other CLI tools that prompt for a password (including Flit and Poetry) didn't turn up any that try to be more helpful.

I suspect those tools don't have wide adoption to the extent that Twine does. We have more users who tend to struggle with exactly this scenario. If you're working at a company and you have an increasing support burden on the product you work on, do you say "Yeah, we'll ignore this because it might add a tad bit of complexity to reduce the time we spend telling customers how to get things to behave as they expect it to based on our existing documentation?" We've fielded many issues about this exact problem. It's not a productive use of your or my severely limited time.

What you're saying here is that you don't value your time or mine enough to fix this. I, on the other hand, do and feel like:

  1. Re-prompting a user is less frustrating to the user than exiting with an error message when we know what to tell the user to do
  2. This current approach isn't going to actually lower the number of new comments or issues we receive about this issue.

If you'd like, I'm happy to put together my own PR for this

@bhrutledge
Copy link
Contributor Author

bhrutledge commented Oct 3, 2021

Having different levels of available information is quite useful. Flake8 leverages this and it helps users find the right level of information when debugging a problem and helps developers of the tool and plugins as well.

I agree, but I think it would take a fair amount of work to implement them, and I'd rather have a solution that offers help to the users in a more immediate/obvious way.

I prefer a lightweight, general indication of "unusable password", rather than spending time writing and maintaining a complex edge case.

Sure, does PyPI have a "minimum length" requirement on passwords? If so len(password) < minimum_length exits early

I'm also wary of re-prompting for password, because Twine currently doesn't do that.

I'd like to understand what makes you wary of this.

PyPI seems to allow a 1-character password, but with a "password strength" meter. Other repositories could have different requirements (including maybe no password at all?). I'd rather let the repository be the source of truth for the validity of the password, and limit Twine to printing potentially helpful information (but ultimately accepting whatever the user enters, instead of exiting or re-prompting). Additionally, the prompting code is already complex, and seems to assume a single input, so I hesitate to bolt on re-prompting.

We've fielded many issues about this exact problem. It's not a productive use of your or my severely limited time.

Agreed.

What you're saying here is that you don't value your time or mine enough to fix this.

That's not what I'm saying, though I see how it could come across that way. I do value our time, and I'm invested in folks being successful with Twine. That's why I opened this PR. However, because of my limited time, I'm trying to be mindful of what we prioritize, and look for solutions that balance improving the user experience with the time it takes to implement them.

With that in mind, and because I'm curious to explore this bit of Python:

I might will try to word-smith a general-purpose logger.warning message if the password is empty/short/unprintable.

@sigmavirus24
Copy link
Member

I agree, but I think it would take a fair amount of work to implement them, and I'd rather have a solution that offers help to the users in a more immediate/obvious way.

So we have different understandings of how long it would take to implement a secure change versus this change. That helps explain the disconnect here.

Additionally, the prompting code is already complex, and seems to assume a single input, so I hesitate to bolt on re-prompting.

I think it's complex primarily because it was refactored prematurely. Again, I don't feel like we're thinking of solving this at the same place and our estimates for difficulty are very different.

@bhrutledge bhrutledge changed the title Show asterisks for password with --verbose Show more helpful message for empty/invalid password Oct 5, 2021
@bhrutledge bhrutledge changed the title Show more helpful message for empty/invalid password Show more helpful messages for invalid passwords Oct 5, 2021
" See https://pypi.org/help/#invalid-auth for more information."
)

return value
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sigmavirus24 What do you think of something like this? I haven't tried it on Windows (yet), but here's what it looks like with an empty password:

Uploading distributions to https://test.pypi.org/legacy/
Enter your username: brian
Enter your password: 
  Your password is empty. Did you enter it correctly?
  See https://pypi.org/help/#invalid-auth for more information.
Uploading twine-3.4.2.dev12+g6694f57.d20210619-py3-none-any.whl
100%|██████████| 41.7k/41.7k [00:00<00:00, 153kB/s] 
For more details, retry the upload with the --verbose option.
HTTPError: 403 Forbidden from https://test.pypi.org/legacy/
Invalid or non-existent authentication information. See https://test.pypi.org/help/#invalid-auth for more information.

If this seems reasonable, I'm happy to add a new section to Twine's docs, to keep this independent of a specific repository.

Copy link
Member

Choose a reason for hiding this comment

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

LGTM

@bhrutledge bhrutledge mentioned this pull request Oct 7, 2021
@bhrutledge bhrutledge marked this pull request as draft October 7, 2021 10:26
enable "Use Ctrl+Shift+C/V as Copy/Paste" in "Properties". This is a
`known issue <https://bugs.python.org/issue37426>`_ with Python's
``getpass`` module.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sigmavirus24 I added this note, and updated the code to it. What do you think?

https://twine--815.org.readthedocs.build/en/815/#using-twine

Copy link
Member

Choose a reason for hiding this comment

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

I might move this up and indent it underneath item 2, but that's more of a nit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did that at first, but it felt distracting. I also think it works better as a direct link outside of the steps.

@bhrutledge bhrutledge marked this pull request as ready for review October 9, 2021 22:23
@bhrutledge bhrutledge merged commit 9d26a5f into pypa:main Oct 10, 2021
@bhrutledge bhrutledge deleted the win-paste-password branch October 10, 2021 01:09
mergify bot pushed a commit to andrewbolster/bolster that referenced this pull request Nov 3, 2021
Bumps [twine](https://github.com/pypa/twine) from 3.4.2 to 3.5.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/pypa/twine/blob/main/docs/changelog.rst">twine's changelog</a>.</em></p>
<blockquote>
<h2>Twine 3.5.0 (2021-11-02)</h2>
<p>Features
^^^^^^^^</p>
<ul>
<li>Show more helpful messages for invalid passwords. (<code>[#815](pypa/twine#815) &lt;https://github.com/pypa/twine/issues/815&gt;</code>_)</li>
<li>Allow the <code>--skip-existing</code> option to work with GCP Artifact Registry. (<code>[#823](pypa/twine#823) &lt;https://github.com/pypa/twine/issues/823&gt;</code>_)</li>
</ul>
<p>Bugfixes
^^^^^^^^</p>
<ul>
<li>Add a helpful error message when an upload fails due to missing a trailing
slash in the URL. (<code>[#812](pypa/twine#812) &lt;https://github.com/pypa/twine/issues/812&gt;</code>_)</li>
<li>Generalize <code>--verbose</code> suggestion when an upload fails. (<code>[#817](pypa/twine#817) &lt;https://github.com/pypa/twine/issues/817&gt;</code>_)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/pypa/twine/commit/7deea5833918cdb8496587119900e8a3cde57dfc"><code>7deea58</code></a> Update changelog for 3.5.0 (<a href="https://github-redirect.dependabot.com/pypa/twine/issues/824">#824</a>)</li>
<li><a href="https://github.com/pypa/twine/commit/f0fc7e8824fe9a93e817c5a7bc1d73f3c9af3ad1"><code>f0fc7e8</code></a> Reorder installation in .readthedocs.yaml (<a href="https://github-redirect.dependabot.com/pypa/twine/issues/825">#825</a>)</li>
<li><a href="https://github.com/pypa/twine/commit/ee97836671f4a494d6b7f7dec21d237a84320e1d"><code>ee97836</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/pypa/twine/issues/823">#823</a> from zware/skip-existing_on_gcp</li>
<li><a href="https://github.com/pypa/twine/commit/34cfed4aac708a9bcb4de04e3fccf619bdadb6c2"><code>34cfed4</code></a> Support --skip-existing with GCP Artifact Registry</li>
<li><a href="https://github.com/pypa/twine/commit/658037f05898b4dc96113628153aa2691b0dbfa3"><code>658037f</code></a> Add intersphinx links for <code>requests</code> (<a href="https://github-redirect.dependabot.com/pypa/twine/issues/819">#819</a>)</li>
<li><a href="https://github.com/pypa/twine/commit/9d26a5f2ec7f176cdad8dea21a4e17d97c962d82"><code>9d26a5f</code></a> Show more helpful messages for invalid  passwords (<a href="https://github-redirect.dependabot.com/pypa/twine/issues/815">#815</a>)</li>
<li><a href="https://github.com/pypa/twine/commit/040a62c749e0015724d93e82ea37bb7e826fef00"><code>040a62c</code></a> Tweak upload --verbose suggestion (<a href="https://github-redirect.dependabot.com/pypa/twine/issues/817">#817</a>)</li>
<li><a href="https://github.com/pypa/twine/commit/28d8571f7e2f908890649f33624fa3bfed7294e1"><code>28d8571</code></a> Add auto-generated API documention (<a href="https://github-redirect.dependabot.com/pypa/twine/issues/810">#810</a>)</li>
<li><a href="https://github.com/pypa/twine/commit/3be41f65176a4ee4909b3307d3705f670dfb0330"><code>3be41f6</code></a> Adding missing docstrings to <code>twine/commands</code> (<a href="https://github-redirect.dependabot.com/pypa/twine/issues/799">#799</a>)</li>
<li><a href="https://github.com/pypa/twine/commit/cf5e0fd7c87d5b1b609c25c59f3ec0d09dc54316"><code>cf5e0fd</code></a> Add a section about proxy support (<a href="https://github-redirect.dependabot.com/pypa/twine/issues/814">#814</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/pypa/twine/compare/3.4.2...3.5.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=twine&package-manager=pip&previous-version=3.4.2&new-version=3.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>
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