Add a Page for the Security Checklist in the Admin View

Review Request #4952 — Created Nov. 13, 2013 and submitted

Information

Review Board
master

Reviewers

With this review request I fixed a lot of style issues. This includes:
- Making sure that includes are properly alphabetized;
- Got rid of escaped characters by making the string double-quoted;
- Renamed variables so they are more consistent;
- Aligned strings;
- Removed outer parenthesis where they are not needed on tuples;
- Improved some of the user-visible descriptions;
- Added a docstring for SecurityCheckRunner;
- Fixed blank lines;
- Fixed URLs;
- Fixed indentation in the template;
- Moved HTML styling into CSS tags.

In terms of actual functionality, I changed the ExecutableCodeCheck test to be able to fail on multiple file types. Before it would end the test on the first failed file type and return that to the user. Now it will return a list of each file type that fails.

I've also included some screenshots to show what various success/failure statuses look like.

Ran through all tests and they succeeded when they should and also failed when they should when I introduced failure conditions.


Description From Last Updated

Since these names are presented to the user, we should mark them for localization. Basically, at the top of this …

daviddavid

This is a good start, but I think we'll want to have this go through a range of different file …

daviddavid

In order to be more consistent with python's unit test frameworks, can we capitalize this "setUp"? In unittest, the setUp …

daviddavid

And "tearDown" here.

daviddavid

This isn't catching any exceptions, and the open() call is leaking an open file. For the open file, you can …

daviddavid

Same thing here with localization. Perhaps call this one "Checking ALLOWED_HOSTS setting" ?

daviddavid

It's a little confusing to have "result" and "results" here. Can we name these better?

daviddavid

I think we're going to want a little bit more complexity in the results than just a true/false value. My …

daviddavid

While I think it would be nice to eventually use entry points for this, for now I think just using …

daviddavid

This probably doesn't need (and shouldn't have) the admin-dashboard ID.

daviddavid

This stuff looks like it was copied from the admin dashboard and can be removed.

daviddavid

Please indent HTML with 1 space.

daviddavid

I don't think you intended to change this file. Please revert it.

daviddavid

No blank space here (both "django" and "djblets" are "third party" with respect to the review board codebase).

daviddavid

Add an extra blank line here.

daviddavid

Remove this blank line.

daviddavid

Remove this blank line.

daviddavid

This is pretty good but I'd like to change the first sentence a bit. How about: "A misconfiguration in the …

daviddavid

Can we add one additional piece of metadata here for instructions to fix it? For this check, we'd want the …

daviddavid

This should probably be `settings.MEDIA_URL + 'uploaded/files/'

daviddavid

We should skip saving, tearing down, and the actual tests in the case that the user is using s3 (or …

daviddavid

This line looks too long (> 80 columns). There's also some changes I'd like to make to the string. Let's …

daviddavid

This should also use _() and string formatting instead of concatenation.

daviddavid

Remove this blank line.

daviddavid

Once again, we should include instructions for how to fix it (shown when the test fails). This one would say …

daviddavid

This string should be marked for translation.

daviddavid

This string, too.

daviddavid

Remove this blank line.

daviddavid

These lines are all indented 4 spaces too many.

daviddavid

Add an extra blank line here.

daviddavid

Should be in alphabetical order (base before storage).

chipx86chipx86

Two blank lines.

chipx86chipx86

These should just default to None.

chipx86chipx86

The camelCase in unittest is a holdover from early days of Python. We shouldn't duplicate it here. Instead, just call …

chipx86chipx86

And teardown.

chipx86chipx86

When you have to escape a single quote, it's best to just use double quotes for the string itself, to …

chipx86chipx86

Can we call this storage instead of fss? It's a more standard variable name for an instance of a Storage …

chipx86chipx86

Would be better as file_checks.

chipx86chipx86

The strings should all be aligned.

chipx86chipx86

Can you break this into two lines, mimicking the newline?

chipx86chipx86

Parameters should be aligned.

chipx86chipx86

The %s will automatically cast to the string, so no need for six.text_type(e) here.

chipx86chipx86

No need for the outer parens here, as it's one line and the comma will ensure it'll be returned as …

chipx86chipx86

Same here.

chipx86chipx86

Parameters need to align.

chipx86chipx86

Not really worth talking about Django. "That Review Board will consider valid for this server" is probably fine.

chipx86chipx86

Make sure the strings align.

chipx86chipx86

Lots of escaping. Should just use double quotes to solve this.

chipx86chipx86

A quicker way would be: if '*' in settings.ALLOWED_HOSTS: result = False ...

chipx86chipx86

Might be worth expanding on this, stating how it'll respond to any host, and how that's unsafe.

chipx86chipx86

No need for these parens.

chipx86chipx86

Mind adding a docstring?

chipx86chipx86

Blank line between these.

chipx86chipx86

Typically, you'd want iteritems(), since it doesn't make a copy of the list of items. Python 3 renames this to …

chipx86chipx86

The }) should align with all_test_results

chipx86chipx86

Should add a trailing comma.

chipx86chipx86

Two blank lines between things on the top level.

chipx86chipx86

Global variables should be at the top of the file, after the imports. Two blank lines on either side.

chipx86chipx86

"Returns" instead of "Gets"

chipx86chipx86

All one line.

chipx86chipx86

"site_domain_method" should only be indented 4 spaces from protocol =

chipx86chipx86

The widget-* URLs are wrong. This regex should be: r'^security/$' The $ prevents security/adfsfsa from matching.

chipx86chipx86

This should be in alphabetical order, so before reviewboard.admin.supports.

chipx86chipx86

Two blank lines.

chipx86chipx86

Should remove this. It'll actually break some server configurations, as many WSGI implementations don't like random things going on stdout.

chipx86chipx86

The })) needs to align with return.

chipx86chipx86

The URLs above based on views.dashboard are done for legacy reasons, but for this one, you should give the URL …

chipx86chipx86

You shouldn't need this. This was only for the graphs on the dashboard.

chipx86chipx86

So a word on indentation in templates. The content of the template, and the template language itself, are indented separately, …

chipx86chipx86

Need a {% trans %}.

chipx86chipx86

Things like border=1, style=, etc. don't belong. These should be defined solely in the CSS. It's not good to mix …

chipx86chipx86

No spaces inside a variable. {{check.name}} would be correct.

chipx86chipx86

This should be localized, and you can use ngettext (included in django.utils.translation) to do the pluralization. It's also ok to …

daviddavid

Two blank lines here.

daviddavid

Can you put quotes around the `*? Also, we've already explained why it's an issue in the desc text, so …

daviddavid

base_site.html already defines this block to be colM, so you shouldn't need this.

daviddavid

I don't think these are necessary, either.

daviddavid

This should include {{block.super}} as the first line of the block.

daviddavid

We shouldn't have any inline styles, only classes and IDs (all styles here should all be in the admin.less file). …

daviddavid

What is this here for?

daviddavid
AS
david
  1. 
      
    1. Can you mark the issues below as fixed (if they are?)

  2. reviewboard/admin/security_checks.py (Diff revision 1)
     
     
    Show all issues

    Since these names are presented to the user, we should mark them for localization.

    Basically, at the top of this file, add this:

    from django.utils.translation import ugettext_lazy as _
    

    and then here:

    name = _("Executable Code Check")
    

    I think we may also want to think about a good user-visible name for this. Maybe Christian has a good idea?

  3. reviewboard/admin/security_checks.py (Diff revision 1)
     
     
    Show all issues

    This is a good start, but I think we'll want to have this go through a range of different file extensions (and file types), instead of just checking .php.

  4. reviewboard/admin/security_checks.py (Diff revision 1)
     
     
     
    Show all issues

    In order to be more consistent with python's unit test frameworks, can we capitalize this "setUp"?

    In unittest, the setUp and tearDown methods are called by the runner, not by the individual test function. We might want to do that here just to make it fit with prior expectations about this pattern.

  5. reviewboard/admin/security_checks.py (Diff revision 1)
     
     
    Show all issues

    And "tearDown" here.

  6. reviewboard/admin/security_checks.py (Diff revision 1)
     
     
     
    Show all issues

    This isn't catching any exceptions, and the open() call is leaking an open file.

    For the open file, you can use the file as a context manager:

    data = urllib2.urlopen(_get_url(self.directory) + to_download).read()
    with self.fss.open(to_download, 'r') as f:
        return data == f.read()
    

    That said, won't the value you get from fss.open(to_download).read() be the same as self.php_file?

  7. reviewboard/admin/security_checks.py (Diff revision 1)
     
     
    Show all issues

    Same thing here with localization. Perhaps call this one "Checking ALLOWED_HOSTS setting" ?

  8. reviewboard/admin/security_checks.py (Diff revision 1)
     
     
     
    Show all issues

    It's a little confusing to have "result" and "results" here. Can we name these better?

  9. reviewboard/admin/security_checks.py (Diff revision 1)
     
     
    Show all issues

    I think we're going to want a little bit more complexity in the results than just a true/false value.

    My instinct here would be to have one of three "results", either pass, fail, or error (if something went wrong during the check), and instructions (or a link to instructions) for how to fix it if it fails.

    We'll probably also want the individual checks to have a description field that can explain to the admin what's getting checked.

    1. I've changed it so that it's still pass/fail, but have added error information. The error information will contain why the test failed - whether it's due to a straight-up test-failure or because there was a problem in the actual test processing. I feel like it's a little cleaner than having the three values. What do you think? I can still change it if that's not ok.

  10. reviewboard/admin/security_checks.py (Diff revision 1)
     
     
     
     
     
     
     
     
     
     
    Show all issues

    While I think it would be nice to eventually use entry points for this, for now I think just using a statically defined dict is sufficient.

    Something like this would also let you get rid of the (un)register_security_check methods, too (which look unused):

    ```python
    _security_checks = {
    'test_check': TestSecurityCheck,
    'executable_check': ExecutableCodeCheck,
    'hosts_check': AllowedHostsCheck,
    }

  11. Show all issues

    This probably doesn't need (and shouldn't have) the admin-dashboard ID.

  12. reviewboard/templates/admin/security.html (Diff revision 1)
     
     
     
     
     
     
     
     
     
     
    Show all issues

    This stuff looks like it was copied from the admin dashboard and can be removed.

  13. reviewboard/templates/admin/security.html (Diff revision 1)
     
     
     
     
     
     
     
    Show all issues

    Please indent HTML with 1 space.

  14. Show all issues

    I don't think you intended to change this file. Please revert it.

  15. 
      
AS
AS
david
  1. 
      
  2. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    No blank space here (both "django" and "djblets" are "third party" with respect to the review board codebase).

  3. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    Add an extra blank line here.

  4. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    Remove this blank line.

  5. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    Remove this blank line.

  6. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
     
     
     
    Show all issues

    This is pretty good but I'd like to change the first sentence a bit. How about:

    "A misconfiguration in the web server can cause files attached to review requests to be executed."

    I'd also change "The types of files being checked" to "The file types being checked".

  7. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    Can we add one additional piece of metadata here for instructions to fix it?

    For this check, we'd want the instructions to link to http://support.beanbaginc.com/support/solutions/articles/110173-securing-file-attachments

  8. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    This should probably be `settings.MEDIA_URL + 'uploaded/files/'

  9. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    We should skip saving, tearing down, and the actual tests in the case that the user is using s3 (or another) storage backend.

    I'd make setUp and tearDown no-ops if settings.DEFAULT_FILE_STORAGE != 'django.core.files.storage.FileSystemStorage' and have execute() just return success.

  10. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    This line looks too long (> 80 columns).

    There's also some changes I'd like to make to the string. Let's add translation, use the "six" library to convert to a text type (for python 2/3 compatibility) and use string formatting to include the exception:

    _('Uncaught exception during test: %s') % six.text_type(e)
    
  11. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    This should also use _() and string formatting instead of concatenation.

  12. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    Remove this blank line.

  13. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    Once again, we should include instructions for how to fix it (shown when the test fails).

    This one would say to edit the settings_local.py file in the site's conf directory and add a line like this with their site's URL:

    ALLOWED_HOSTS = ['example.com']

  14. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    This string should be marked for translation.

  15. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    This string, too.

  16. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    Remove this blank line.

  17. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
     
     
     
     
    Show all issues

    These lines are all indented 4 spaces too many.

  18. reviewboard/admin/security_checks.py (Diff revision 2)
     
     
    Show all issues

    Add an extra blank line here.

  19. 
      
AS
chipx86
  1. A handful of comments, but the good news is that they're almost entirely stylistic things.

    Can we see some screenshots showing success, failure, and a mix?

  2. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
    Show all issues

    Should be in alphabetical order (base before storage).

  3. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
     
    Show all issues

    Two blank lines.

  4. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
     
    Show all issues

    These should just default to None.

  5. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    The camelCase in unittest is a holdover from early days of Python. We shouldn't duplicate it here. Instead, just call this setup.

  6. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    And teardown.

  7. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    When you have to escape a single quote, it's best to just use double quotes for the string itself, to avoid the escape.

  8. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    Can we call this storage instead of fss? It's a more standard variable name for an instance of a Storage class.

  9. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    Would be better as file_checks.

  10. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
     
     
     
     
     
     
     
     
    Show all issues

    The strings should all be aligned.

  11. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    Can you break this into two lines, mimicking the newline?

  12. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
     
    Show all issues

    Parameters should be aligned.

  13. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    The %s will automatically cast to the string, so no need for six.text_type(e) here.

  14. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     

    Just to check for myself, this will fail on the first failed file, right?

    How hard would it be to still try every type, and present a list of failed file extensions? That'd prevent the admin from having to make a change, reload the web server, try the checks again, and repeating.

  15. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    No need for the outer parens here, as it's one line and the comma will ensure it'll be returned as a tuple.

  16. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    Same here.

  17. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
    Show all issues

    Parameters need to align.

  18. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    Not really worth talking about Django. "That Review Board will consider valid for this server" is probably fine.

  19. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
     
     
     
     
     
     
    Show all issues

    Make sure the strings align.

  20. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
     
    Show all issues

    Lots of escaping. Should just use double quotes to solve this.

  21. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
    Show all issues

    A quicker way would be:

    if '*' in settings.ALLOWED_HOSTS:
        result = False
        ...
    
  22. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    Might be worth expanding on this, stating how it'll respond to any host, and how that's unsafe.

  23. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    No need for these parens.

  24. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    Mind adding a docstring?

  25. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
    Show all issues

    Blank line between these.

  26. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    Typically, you'd want iteritems(), since it doesn't make a copy of the list of items.

    Python 3 renames this to items(), so we're now using this six module to provide abstraction.

    So, this should be:

    for name, cls in six.iteritems(checks):
    
  27. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
     
     
     
     
     
    Show all issues

    The }) should align with all_test_results

  28. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    Should add a trailing comma.

  29. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
     
    Show all issues

    Two blank lines between things on the top level.

  30. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    Global variables should be at the top of the file, after the imports. Two blank lines on either side.

  31. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
    Show all issues

    "Returns" instead of "Gets"

  32. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
     
    Show all issues

    All one line.

  33. reviewboard/admin/security_checks.py (Diff revision 3)
     
     
     
    Show all issues

    "site_domain_method" should only be indented 4 spaces from protocol =

  34. reviewboard/admin/urls.py (Diff revision 3)
     
     
    Show all issues

    The widget-* URLs are wrong. This regex should be:

    r'^security/$'
    

    The $ prevents security/adfsfsa from matching.

  35. reviewboard/admin/views.py (Diff revision 3)
     
     
    Show all issues

    This should be in alphabetical order, so before reviewboard.admin.supports.

  36. reviewboard/admin/views.py (Diff revision 3)
     
     
     
     
    Show all issues

    Two blank lines.

  37. reviewboard/admin/views.py (Diff revision 3)
     
     
    Show all issues

    Should remove this. It'll actually break some server configurations, as many WSGI implementations don't like random things going on stdout.

  38. reviewboard/admin/views.py (Diff revision 3)
     
     
     
     
    Show all issues

    The })) needs to align with return.

  39. Show all issues

    The URLs above based on views.dashboard are done for legacy reasons, but for this one, you should give the URL a name in the urls.py file by doing:

    url(r'^security/$', 'security', name='admin-security-checks')
    

    Then, here, you can do:

    {% url 'admin-security-checks' %}
    
  40. Show all issues

    You shouldn't need this. This was only for the graphs on the dashboard.

  41. reviewboard/templates/admin/security.html (Diff revision 3)
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
    Show all issues

    So a word on indentation in templates.

    The content of the template, and the template language itself, are indented separately, as there are times that they don't actually match up, and for some file types it would be flat-out wrong.

    So, template tags are indented like:

    {% block foo %}
    {%  if bar %}
    {%   for foobar in bar %}
    ...
    {%   endfor %}
    {%  endif %}
    {% endblock %}
    

    One space indentation within the template tag markup, relative to the parent template tag.

    HTML tags should be indented one space relative to their parent tag, and not to the template tag.

  42. Show all issues

    Need a {% trans %}.

  43. Show all issues

    Things like border=1, style=, etc. don't belong. These should be defined solely in the CSS. It's not good to mix presentation and content in HTML.

  44. Show all issues

    No spaces inside a variable. {{check.name}} would be correct.

  45. 
      
AS
  1. 
      
  2. reviewboard/admin/security_checks.py (Diff revision 3)
     
     

    After discussion we determined it would be better to leave it in camelCase.

  3. reviewboard/admin/security_checks.py (Diff revision 3)
     
     

    Same as above.

  4. 
      
AS
david
  1. 
      
  2. reviewboard/admin/security_checks.py (Diff revision 4)
     
     
     
     
    Show all issues

    This should be localized, and you can use ngettext (included in django.utils.translation) to do the pluralization. It's also ok to use join on a single-element list, in which case it will just return the one element, regardless of joiner.

    I'd also like to tweak this text a bit.

    error_msg = (
        ngettext(
            'The web server incorrectly executed these file types: %s',
            'The web server incorrectly executed this file type: %s',
            len(failed_exts))
        % ', '.join(failed_exts))
    
  3. reviewboard/admin/security_checks.py (Diff revision 4)
     
     
    Show all issues

    Two blank lines here.

  4. reviewboard/admin/security_checks.py (Diff revision 4)
     
     
     
     
    Show all issues

    Can you put quotes around the `*?

    Also, we've already explained why it's an issue in the desc text, so this probably doesn't need to have the second sentence at all.

    1. Decided that we'll leave it as 'ALLOWED_HOSTS contains '*', which means that the server will respond to any host.'
      This is a little more descriptive about the error result specifically but is less redundant when considering the description.

  5. Show all issues

    base_site.html already defines this block to be colM, so you shouldn't need this.

  6. reviewboard/templates/admin/security.html (Diff revision 4)
     
     
     
    Show all issues

    I don't think these are necessary, either.

  7. reviewboard/templates/admin/security.html (Diff revision 4)
     
     
     
    Show all issues

    This should include {{block.super}} as the first line of the block.

  8. reviewboard/templates/admin/security.html (Diff revision 4)
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
    Show all issues

    We shouldn't have any inline styles, only classes and IDs (all styles here should all be in the admin.less file).

    Having the error message be a single-element <ul> is pretty weird. Can we just make it a text span?

    I'd also like to consider not doing this as a table with borders, but instead spacing it out with whitespace and headings. That said, changing the look is lower priority than the other fixes.

  9. Show all issues

    What is this here for?

  10. 
      
AS
david
  1. The code looks pretty good to me (aside from the CSS and layout changes, which we talked about). It looks like your review request description somehow got overwritten with a description of the changes in diff r4. Can you change the description on the review request to be a description of what the feature is and an overview of what code changes were done to implement it?

  2. 
      
david
  1. I've gone through and updated the style and layout of the checklist page a bit to make it less boxy and more readable. I'll push this to master once 2.0 beta 2 is out.

    1. Actually, it'll be in beta 2. Woohoo!

  2. 
      
AS
Review request changed
Status:
Completed
Change Summary:
Pushed to master (807a2a3).