Skip to content

SSL certificate verification. #791

Merged
merged 59 commits into from Feb 19, 2013

9 participants

@qwcode
Python Packaging Authority member
qwcode commented Feb 6, 2013

builds on original pull #789

Done:

  • move ssl and match_hostname imports to backwardcompat
  • added match_hostname function to pip (no external dependency)
  • cert path moved to locations module
  • better error message when UrlError.reason is CertificateError or SSLError
  • added license for CA Root ceriticates
  • miscellaneous test fixes
  • --cert-path option to override path to pem file
  • add new tests
  • when scheme is 'https' and have ssl module, add in ssl cert verifying handler (and exclude http handler). otherwise, use build_opener to generate a "standard" chain of handlers.
  • --allow-no-ssl option to allow lack of cert verification when no ssl module (for py25 users who won't install ssl)
  • update to latest certs securely (https://gist.github.com/jjb/996292)
  • update authors file and release notes
  • update pip docs
  • py25 socket patch to work with ssl backport
  • only show --allow-no-ssl when no ssl
  • use the words "CA bundle" instead of "certificate file"
  • get an install test working that installs ssl backport for py25
  • refactor backwardcompat to a package
  • py25 ssl backport install test works locally, but skipping on travis for now.
  • fix the "pip list" tests to use our local packages for testing
  • wait for PSF's new cert, then remove old cacert.org cert from pem file
  • http://cheeseshop.python.org/ 503 errors have been fixed
  • updates based on jannis review

Todo:

  • merge and release an RC.
@rasky rasky and 4 others commented on an outdated diff Feb 6, 2013
@@ -664,3 +666,18 @@ def call_subprocess(cmd, show_stdout=True,
% (command_desc, proc.returncode, cwd))
if stdout is not None:
return ''.join(all_output)
+
+
+def warn_if_no_ssl():
+ """Warn when there's no ssl"""
+ if not ssl:
+ logger.warn(textwrap.dedent("""
+ #############################################################
+ ## WARNING!! ##
+ ## You don't have an importable ssl module. ##
+ ## We can not provide ssl certified downloads from PyPI. ##
+ ## Install this: http://pypi.python.org/pypi/ssl/ ##
@rasky
rasky added a note Feb 6, 2013

You might want to use "https" in this URL :)

@qwcode
Python Packaging Authority member
qwcode added a note Feb 6, 2013

yes : )

@rasky
rasky added a note Feb 6, 2013

As an additional note, I think that we should probably wait for a keypress, or wait either 10 seconds or a keypress, (or exit and ask to be relaunched with a command line option, eg: --exploit-me). I know it's annoying, but there is a remote code execution vulnerability here; if you simply print a warning and get on downloading the file and running its setup.py, it might be too late for the user.

@qwcode
Python Packaging Authority member
qwcode added a note Feb 6, 2013

I'll go with the consensus, but I was concerned about breaking people's automated processes. @jezdez, @pnasrat ?

@rasky
rasky added a note Feb 6, 2013

If that's an (understandable) concern, a delay of 10 seconds (shortened by a keypress) should probably solve it.

@pnasrat
Python Packaging Authority member
pnasrat added a note Feb 6, 2013

I don't like --exploit-me as an option just --nossl is probably sufficient. We probably don't want to break scripts, just announce vocally.

For package maintainers we should also have this in the installation docs.

@qwcode
Python Packaging Authority member
qwcode added a note Feb 6, 2013

@pnasrat but if they don't use --nossl ?

which option?
1) just a message with no delay
2) 10s delay which gives people time to quit or continue
3) fail

@dstufft
Python Packaging Authority member
dstufft added a note Feb 6, 2013

If they don't explictly disable SSL fail w/ an error telling them how to bypass it.

@jezdez
jezdez added a note Feb 6, 2013

3)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@dstufft
Python Packaging Authority member
dstufft commented Feb 6, 2013

We should ensure that any SSL enabled index url is only ever accessed via SSL. In particular this protects against a SSL stripping[1] attack where an attacker is able to convert the request to plaintext (and thus be able to modify the request) before sending it via SSL itself to PyPI.

[1] http://www.thoughtcrime.org/software/sslstrip/

@rasky
rasky commented Feb 6, 2013

@dstufft I don't think it's a problem in the context of the patch since the PyPI index URL does not (currently) redirect to the HTTPS version, and pip will hardcode the HTTPS version anyway.

@dstufft
Python Packaging Authority member
dstufft commented Feb 6, 2013

@rasky What will happen if pip requests the HTTPS version but receives a plaintext response?

@rasky
rasky commented Feb 6, 2013

@dstufft Given that the patch explicitly wraps the TCP socket with ssl.wrap_socket, the SSL negotiation would fail because they won't speak the same protocol ( = ssl.wrap_socket() will raise an exception).

This said, please do test it :)

@dstufft
Python Packaging Authority member
@radiosilence

With regards to CAcert, is the strategy to go with CAcert "for now", and then remove it once PyPI has a good certificate?

For a free Class 1 (no EV) certificate, I'd definitely look into StartSSL.

@qwcode
Python Packaging Authority member
qwcode commented Feb 7, 2013

I think PyPI is close to having a new cert. The idea right now, is that I would remove the cacert.org cert before merge.

@rasky rasky commented on an outdated diff Feb 7, 2013
@@ -664,3 +666,18 @@ def call_subprocess(cmd, show_stdout=True,
% (command_desc, proc.returncode, cwd))
if stdout is not None:
return ''.join(all_output)
+
+
+def raise_no_ssl_exception():
+ """Raise when there's no ssl and not using '--no-ssl'"""
+ raise PipError(textwrap.dedent("""
+ #############################################################
+ ## You don't have an importable ssl module. ##
+ ## We can not provide ssl certified downloads. ##
+ ## Do one of 2 things: ##
+ ## 1) Install this: https://pypi.python.org/pypi/ssl/ ##
+ ## (It provides ssl support for older Pythons ) ##
+ ## 2) Use the --no-ssl option to allow this insecurity ##
@rasky
rasky added a note Feb 7, 2013

It's --allow-no-ssl now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@rasky rasky commented on the diff Feb 7, 2013
tests/test_config.py
reset_env(environ)
result = run_pip('install', '-vvv', 'INITools', expect_error=True)
assert "Analyzing links from page http://pypi.pinaxproject.com" in result.stdout, result.stdout
- assert "Analyzing links from page http://example.com" in result.stdout, result.stdout
+ assert "Skipping link %s" % find_links in result.stdout
@rasky
rasky added a note Feb 7, 2013

Since we're at it: shouldn't --find-links also require --allow-no-ssl? It can be a separate patch, of course (I can work on it).

@dstufft
Python Packaging Authority member
dstufft added a note Feb 7, 2013

Forcing SSL on find links doesn't really buy near as much as forcing it on PyPI does. Further more we don't know for sure that find links will even have SSL (or if it is, valid SSL), and we don't want to have people disable SSL for their Find links and have it disable their main repo protection as well.

@rasky
rasky added a note Feb 7, 2013

Well, the URL pointed by --find-links can still be MITM'd to serve compromised packages, as far as I can see. Obviously it's less important than fixing the PyPI one, but it's probably something to consider while we enforce pip security.

Does --find-links work with HTTPS urls? Maybe we should emit a warning if the URL being passed is HTTP?

@dstufft
Python Packaging Authority member
dstufft added a note Feb 7, 2013

I think a warning with HTTP would def be good. --find-links requires user actions to enable it so we'll be secure out of the box still. The biggest concern I have with tying it to --allow-no-ssl is losing PyPI SSL protection b/c one of your find links doesn't have a valid SSL cert.

@qwcode
Python Packaging Authority member
qwcode added a note Feb 7, 2013

the way it's written now, the use of the ssl url opener is for any connection, index urls or find_link urls, whether they are "http" or "https"

and --allow-no-ssl just means "it's ok to use the non-ssl url opener when ssl is not importable".

e.g.:

pip -vv  install --no-index --find-links=https://pypi.python.org/simple/peppercorn/ peppercorn
[...]
 Analyzing links from page https://pypi.python.org/simple/peppercorn/
[...]
 Found link https://pypi.python.org/packages/source/p/peppercorn/peppercorn- [..]

this all seems to work, but should it be changed? should we use the non-ssl opener for "http:" urls?

@qwcode
Python Packaging Authority member
qwcode added a note Feb 7, 2013

should we use the non-ssl opener for "http:" urls?

when accessing "http" urls, our opener (a normal opener plus the https handler) will try and use the http handler first (and succeed), before exercising the https handler (which it would fail on), so I think it's ok.

@rasky
rasky added a note Feb 7, 2013

Oh, that's a security issue then. It means that an attacker can still MITM by having a non-SSL HTTP proxy server on port 443.

The opener used by pip to access PyPI (and all https URLs) should only allow SSL connections; there should not be any fallback to HTTP (not even if SSL fails).

@qwcode
Python Packaging Authority member
qwcode added a note Feb 7, 2013

ok, if it really does fail in this case (and I agree it seems it could given the way it processes thru the chain of handlers; just me looking at urlllib2 code; not an actual experiment), I think it can be prevented pretty easily, by passing in our own dummy HTTPHandler that fails in all cases, in addition to our verification handler.

from the urllib2 docs for build_opener:
"Instances of the following classes will be in front of the handlers, unless the handlers contain them, instances of them or subclasses of them: ProxyHandler, UnknownHandler, HTTPHandler, HTTPDefaultErrorHandler, HTTPRedirectHandler, FTPHandler, FileHandler, HTTPErrorProcessor"

@qwcode
Python Packaging Authority member
qwcode added a note Feb 7, 2013

also, to be clear, there is no "fallback" to the http handler if the https handler fails. the issue is that it seems to try the http handler first.

I guess the most direct thing is to specifically contruct an OpenerDirector instance with one handler, and not use build_opener

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@rasky rasky and 1 other commented on an outdated diff Feb 7, 2013
pip/basecommand.py
@@ -106,6 +107,12 @@ def main(self, args, initial_options):
if options.exists_action:
os.environ['PIP_EXISTS_ACTION'] = ''.join(options.exists_action)
+ if options.allow_no_ssl:
+ os.environ['PIP_ALLOW_NO_SSL'] = '1'
@rasky
rasky added a note Feb 7, 2013

Is there any security implication in using the environmentfor security-related configuration (PIP_ALLOW_NO_SSL, PIP_CERT_PATH)? I guess that if an attacker can modify the environment in which pip is run, she can as well change the PYTHONPATH to her hacked version of pip, so maybe it's not a big problem.

Comments?

@dstufft
Python Packaging Authority member
dstufft added a note Feb 7, 2013

I wouldn't be real worried about it. If an attacker can set envvars we've lost the game for that computer already.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@hltbra hltbra and 1 other commented on an outdated diff Feb 7, 2013
tests/test_pip.py
@@ -11,6 +11,10 @@
from scripttest import TestFileEnvironment, FoundDir
from tests.path import Path, curdir, u
from pip.util import rmtree
+from pip.backwardcompat import ssl
+
+#allow py25 unit tests to work
+os.environ['PIP_ALLOW_NO_SSL'] = '1'
@hltbra
Python Packaging Authority member
hltbra added a note Feb 7, 2013

Should all tests skip ssl by default? I see there is the condition to include that if no ssl module found at run_pip, it should not be here (at top), right?

@qwcode
Python Packaging Authority member
qwcode added a note Feb 7, 2013

--allow-no-ssl means that it's ok to use the non-ssl url opener when ssl is not installed (which is only true on py25)
it doesn't mean "don't use ssl". to be clear though, I will add a condition such that's only set for py25

also, I'm going to add a specific test case that runs just for py25 that installs ssl (http://pypi.python.org/pypi/ssl/) and confirm that an install against pypi works using that library

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@qwcode qwcode commented on an outdated diff Feb 9, 2013
pip/download.py
+ self.sock = ssl.wrap_socket(sock,
+ self.key_file,
+ self.cert_file,
+ cert_reqs=ssl.CERT_REQUIRED,
+ ca_certs=cert_path)
+ match_hostname(self.sock.getpeercert(), self.host)
+
+
+class VerifiedHTTPSHandler(urllib2.HTTPSHandler, urllib2.HTTPHandler):
+ """
+ A HTTPSHandler that wraps connections with ssl certificate verification.
+ By inheriting from both HTTPSHandler and HTTPHandler, this overrides
+ the default https *and* http handlers during the 'build_opener' routine.
+ We specifically *don't* want a http handler in the chain of handlers
+ to prevent MITM attacks that spoof https servers with http content.
+ """
@qwcode
Python Packaging Authority member
qwcode added a note Feb 9, 2013

although the double inheritance works to eliminate the httphandler, this feels weird.
maybe just better to delete the httphandler from the handler list after the OpenerDirector instance is built.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@rasky rasky and 1 other commented on an outdated diff Feb 9, 2013
pip/baseparser.py
@@ -350,4 +350,20 @@ def create_main_parser():
metavar='action',
help="Default action when a path already exists: "
"(s)witch, (i)gnore, (w)ipe, (b)ackup."),
+
+ optparse.make_option(
+ '--allow-no-ssl',
+ dest='allow_no_ssl',
+ action='store_true',
+ default=False,
+ help = "Allow lack of certificate checking when ssl is not installed."),
+
+ optparse.make_option(
+ '--cert-path',
+ dest='cert_path',
+ type='str',
+ default='',
+ metavar='path',
+ help = "Path to alternate certificate file."),
@rasky
rasky added a note Feb 9, 2013

The most common name is probably "CA bundle" instead of "certificate file". If I read certificate file, I think of a single certificate (and thus I cannot understand why pip would need a certificate).

@qwcode
Python Packaging Authority member
qwcode added a note Feb 9, 2013

ok, I'll change the help line. "Path to alternate CA bundle."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@rasky rasky and 1 other commented on an outdated diff Feb 9, 2013
pip/baseparser.py
@@ -350,4 +350,20 @@ def create_main_parser():
metavar='action',
help="Default action when a path already exists: "
"(s)witch, (i)gnore, (w)ipe, (b)ackup."),
+
+ optparse.make_option(
+ '--allow-no-ssl',
+ dest='allow_no_ssl',
+ action='store_true',
+ default=False,
+ help = "Allow lack of certificate checking when ssl is not installed."),
@rasky
rasky added a note Feb 9, 2013

Shouldn't this option itself be guarded by if ssl of if py25, so not to clutter the --help output for most users? It does not have any effect for them anyway.

@qwcode
Python Packaging Authority member
qwcode added a note Feb 9, 2013

agreed, I'll do that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jezdez
jezdez commented Feb 10, 2013

@qwcode Would you mind moving the backwardcompat_ssl.py into backwardcompat/ssl_match_hostname.py please?

@qwcode
Python Packaging Authority member
qwcode commented Feb 10, 2013

@jezdez, sure, np.

@qwcode
Python Packaging Authority member
qwcode commented Feb 10, 2013

@jezdez, you mentioned on twitter, pip using the requests project for ssl verification. is that effort happening somewhere? should we reconsider this pull?

@hltbra
Python Packaging Authority member
hltbra commented Feb 16, 2013

What about pip search that uses XMLRPC and HTTP?

@coderanger

New SSL layout on PyPI is live. Gogogo :runner: :runner: :runner:

@qwcode
Python Packaging Authority member
qwcode commented Feb 16, 2013
@qwcode
Python Packaging Authority member
qwcode commented Feb 16, 2013

sorting out test failures that are arising as a result....
couple of tests end up hitting http://cheeseshop.python.org which now returns 503...
intentional?

@jezdez
jezdez commented on 1857ece Feb 17, 2013

D'oh!

@jezdez jezdez commented on the diff Feb 17, 2013
pip/backwardcompat/socket_create_connection.py
+ host, port = address
+ err = None
+ for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
+ af, socktype, proto, canonname, sa = res
+ sock = None
+ try:
+ sock = socket.socket(af, socktype, proto)
+ if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
+ sock.settimeout(timeout)
+ if source_address:
+ sock.bind(source_address)
+ sock.connect(sa)
+ return sock
+
+ except socket.error:
+ err = sys.exc_info()[1]
@jezdez
jezdez added a note Feb 17, 2013

sys is undefined here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jezdez jezdez commented on an outdated diff Feb 17, 2013
@@ -0,0 +1,18 @@
@jezdez
jezdez added a note Feb 17, 2013

This file can be ported over to LICENSE.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jezdez jezdez commented on an outdated diff Feb 17, 2013
docs/installing.txt
@@ -3,11 +3,21 @@
Installation
============
-Python Support
---------------
+.. warning::
+
+ Prior to version 1.3, pip did not use SSL for downloading packages from PyPI, and thus left
+ users more vulnerable to security threats. We advise installing at least version 1.3.
+ If you're using `virtualenv <http://www.virtualenv.org>`_ to install pip, we advise installing
+ at least version 1.8.5, which contains pip version 1.3.
@jezdez
jezdez added a note Feb 17, 2013

I think we should make a 1.9 release to mark pip 1.3 inclusion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jezdez jezdez and 2 others commented on an outdated diff Feb 17, 2013
docs/installing.txt
+
+Using a Package Manager
++++++++++++++++++++++++
+
+On Linux, pip is packaged by most distributions. For instance, on an Ubuntu
+system, you can install it with::
+
+ $ sudo apt-get install python-pip
+
+On a Fedora system, you can install it with::
+
+ $ yum install python-pip
+
+The latest versions of pip may not be available using this method.
+
+
@jezdez
jezdez added a note Feb 17, 2013

The package manager section is clearly to help people get the safest experience. The problem though is that they almost always have older versions of pip available (e.g. http://packages.ubuntu.com/search?keywords=python-pip) that would make the latest efforts to secure pip moot. Please remove this section till we have confirmation that the latest pip is available through the native package managers.

@rasky
rasky added a note Feb 17, 2013

Just before this paragraph, there is a warning explaining that you really want pip 1.3.

What about adding a line saying "make sure that your distribution packages pip 1.3 or later, otherwise use a manual installation as explained below".

@jezdez
jezdez added a note Feb 17, 2013

See, I don't believe people actually read all the paragraphs, but only look for the apt-get line they need to copy/paste. So even providing that part is a no-go for me.

That said, maybe we should reach out to the distros activately and ask them to update. Or.. even provide packages for the different distros (e.g. an own PPA).

@pnasrat
Python Packaging Authority member
pnasrat added a note Feb 17, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jezdez jezdez commented on an outdated diff Feb 17, 2013
docs/logic.txt
@@ -151,17 +151,37 @@ pip offers a set of :ref:`Package Index Options <Package Index Options>` for mod
See the :ref:`pip install Examples<pip install Examples>`.
+.. _`SSL Certificate Verification`:
+
+SSL Certificate Verification
+============================
+
+Starting with v1.3, pip provides SSL certificate verification over https, for the purpose
+of providing secure, certified downloads from PyPI.
+
+This is supported by default in all Python versions pip supports, except Python 2.5.
+
+Python 2.5 users can install https://pypi.python.org/pypi/ssl/, which provides ssl support for older pythons.
@jezdez
jezdez added a note Feb 17, 2013

We should extend that paragraph a bit, with a step by step guide how to install the ssl package for *NIX and Windows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jezdez jezdez commented on an outdated diff Feb 17, 2013
pip/basecommand.py
@@ -69,11 +69,15 @@ def _copy_option_group(self, parser, group):
def merge_options(self, initial_options, options):
# Make sure we have all global options carried over
- for attr in ['log', 'proxy', 'require_venv',
+ attrs = ['log', 'proxy', 'require_venv',
'log_explicit_levels', 'log_file',
@jezdez
jezdez added a note Feb 17, 2013

Indentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jezdez jezdez commented on an outdated diff Feb 17, 2013
pip/basecommand.py
@@ -104,6 +108,12 @@ def main(self, args, initial_options):
if options.exists_action:
os.environ['PIP_EXISTS_ACTION'] = ''.join(options.exists_action)
+ if not ssl and options.allow_no_ssl:
+ os.environ['PIP_ALLOW_NO_SSL'] = '1'
@jezdez
jezdez added a note Feb 17, 2013

Why do we need to set an environment variable here? The option system in pip was built so it can accept env vars and pass them as part of the optparse options to the functions that need it, not the other way around.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jezdez jezdez and 1 other commented on an outdated diff Feb 17, 2013
pip/basecommand.py
@@ -104,6 +108,12 @@ def main(self, args, initial_options):
if options.exists_action:
os.environ['PIP_EXISTS_ACTION'] = ''.join(options.exists_action)
+ if not ssl and options.allow_no_ssl:
+ os.environ['PIP_ALLOW_NO_SSL'] = '1'
+
+ if options.cert_path:
+ os.environ['PIP_CERT_PATH'] = options.cert_path
@jezdez
jezdez added a note Feb 17, 2013

Same as above.

@qwcode
Python Packaging Authority member
qwcode added a note Feb 17, 2013

these options are used in places where the options object in the command isn't currently tunneled into.
we'd need to pass options down a couple of levels. what do you recommend?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jezdez jezdez commented on an outdated diff Feb 17, 2013
pip/baseparser.py
@@ -356,4 +356,21 @@ def create_main_parser():
metavar='action',
help="Default action when a path already exists: "
"(s)witch, (i)gnore, (w)ipe, (b)ackup."),
+
+ optparse.make_option(
+ '--cert-path',
@jezdez
jezdez added a note Feb 17, 2013

Should we maybe just call that --cert, less to type :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jezdez jezdez commented on an outdated diff Feb 17, 2013
pip/baseparser.py
]
+
+if not ssl:
+ standard_options.append(optparse.make_option(
+ '--allow-no-ssl',
@jezdez
jezdez added a note Feb 17, 2013

For this option I'd like to make it even clearer to the user what they are doing if they don't use this. Let's call it --insecure or some-such.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jezdez jezdez commented on an outdated diff Feb 17, 2013
@@ -664,3 +666,18 @@ def call_subprocess(cmd, show_stdout=True,
% (command_desc, proc.returncode, cwd))
if stdout is not None:
return ''.join(all_output)
+
+
+def raise_no_ssl_exception():
+ """Raise when there's no ssl and not using '--no-ssl'"""
+ raise PipError(textwrap.dedent("""
+ #############################################################
+ ## You don't have an importable ssl module. ##
+ ## We can not provide ssl certified downloads. ##
+ ## Do one of 2 things: ##
+ ## 1) Install this: https://pypi.python.org/pypi/ssl/ ##
+ ## (It provides ssl support for older Pythons ) ##
+ ## 2) Use the --allow-no-ssl option to allow insecurity ##
+ #############################################################
@jezdez
jezdez added a note Feb 17, 2013

This is pretty good already, I fear that people will freak out if they see it for the first time though. Let's explain why we can't ship ssl support and link to our documentation explaining in detail how to install it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@qwcode
Python Packaging Authority member
qwcode commented Feb 17, 2013

just read @jezdez comments. will post updates later....

@jezdez
jezdez commented Feb 17, 2013

w00t!, @qwcode!

@qwcode
Python Packaging Authority member
qwcode commented Feb 18, 2013

@hltbra , for pip search, I can switch the index url to 'https', but the xmlrpc lib is written internally to use the HTTP connection, and since we're not downloading using that command, I wasn't going to do much work right now to use a cert verifying connection. not sure it would work?

@jezdez
jezdez commented on 2cbc7fa Feb 18, 2013

Win!

@qwcode
Python Packaging Authority member
qwcode commented Feb 18, 2013

signing off for now.

will check tomorrow for any more updates to be made.
hoping to be able cut RCs tomorrow.

@merwok

Indent has two extra spaces.

@qwcode qwcode merged commit beb9dea into pypa:develop Feb 19, 2013

1 check was pending

Details default The Travis build is in progress
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.