compare: correct display of special branch names in initial placeholder
When a branch name contains special characters like '<' or '>', and a 'compare' operation is performed with such branch as one of the two compare sides, then the special branch name will be part of the URL, e.g.
The encoded branch name is then used at page load as placeholders for the branch selection dropdowns. But, the special characters, were escaped too much, causing '<' to become < in the display of the dropdown.
The placeholder was escaped via the default mako escape filter, before being passed to make_revision_dropdown, thus too early. We want the raw value. h.js() (copied from the default branch) gives us that, while still formatting and escaping the string so it is safe inside the script tag.
compare: prevent XSS due to unescaped branch/tag/bookmark names
In the revision selection dropdown of the 'Compare' functionality, the branch/tag/bookmark names were not correctly escaped.
This means that if an attacker is able to push a branch/tag/bookmark containing HTML/JavaScript in its name, then that code would be evaluated. This is a cross-site scripting (XSS) vulnerability.
Fix the problem by correctly escaping the branch/tag/bookmarks.
templates/summary: escape branch/tag/bookmark names in 'Download as zip' links to prevent XSS
On a repository summary page, in the 'Download' section where you can download an archive of the repository at a given revision, the branch/tag names were not correctly escaped.
This means that if an attacker is able to push a branch/tag/bookmark containing HTML/JavaScript in its name, then that code would be evaluated. This is a cross-site scripting (XSS) vulnerability.
Fix the problem by correctly escaping the branch/tag/bookmarks.
Reported by Bob Hogg <wombat@rwhogg.site> (thanks!).
lib: sanitize HTML for all types of README rendering, not only markdown
The repository summary page will display a rendered version of the repository 'readme' based on its file extension. In commit 5746cc3b3fa5, the rendered output was already sanitized when the input was markdown. However, also readmes written in other formats, like ReStructuredText (RST) or plain text could have content that we want sanitized.
Therefore, move the sanitizing one level up so it covers all renderers, for now and the future.
This fixes an XSS issue when a repository readme contains javascript code, which would be executed when the repository summary page is visited by a user.
Reported by Bob Hogg <wombat@rwhogg.site> (thanks!).
cleanup: remove unnecessary (and potentially problematic) use of 'literal'
webhelpers.html.literal (kallithea.lib.helpers.literal) is only needed when the passed string may contain HTML that needs to be interpreted literally. It is unnecessary for plain strings.
Incorrect usage of literal can lead to XSS issues, via a malicious user controlling data which will be rendered in other users' browsers. The data could either be stored previously in the system or be part of a forged URL the victim clicks on.
For example, when a user browses to a forged URL where a repository changeset or branch name contains a javascript snippet, the snippet was executed when printed on the page using 'literal'.
Remaining uses of 'literal' have been reviewed with no apparent problems found.
Reported by Bob Hogg <wombat@rwhogg.site> (thanks!).
pullrequests: prevent XSS in 'Potential Reviewers' list when first and last names cannot be trusted
If a user first or last name contains javascript, these fields need proper escaping to avoid XSS attacks.
An example scenario is: - the malicious user creates a repository. This will cause this user to be listed automatically under 'Potential Reviewers' in pull requests. - another user creates a pull request on that repository and selects the suggested reviewer from the 'Potential Reviewers' list.
Reported by Bob Hogg <wombat@rwhogg.site> (thanks!).
Technical note: the other caller of addReviewMember in base.js itself does _not_ need to be adapted to escape the input values, because the input values (oData) are _already_ escaped (by the YUI framework).
compare: correct display of special branch names in initial placeholder
When a branch name contains special characters like '<' or '>', and a 'compare' operation is performed with such branch as one of the two compare sides, then the special branch name will be part of the URL, e.g.
The encoded branch name is then used at page load as placeholders for the branch selection dropdowns. But, the special characters, were escaped too much, causing '<' to become < in the display of the dropdown.
It was not correct to use h.jshtml() to escape in the template. That applied html formatting, too much and too early. We want the raw value. h.js() gives us that, while still formatting and escaping the string so it is safe inside the script tag.
compare: prevent XSS due to unescaped branch/tag/bookmark names
In the revision selection dropdown of the 'Compare' functionality, the branch/tag/bookmark names were not correctly escaped.
This means that if an attacker is able to push a branch/tag/bookmark containing HTML/JavaScript in its name, then that code would be evaluated. This is a cross-site scripting (XSS) vulnerability.
Fix the problem by correctly escaping the branch/tag/bookmarks.
base: escape branch/tag/bookmark names in 'Switch To' menu to prevent XSS
On repository pages, the 'Switch To' did not escape branches correctly.
This means that if an attacker is able to push a branch/tag/bookmark containing HTML/JavaScript in its name, then that code would be evaluated. This is a cross-site scripting (XSS) vulnerability.
Fix the problem by correctly escaping the branch/tag/bookmarks with .html_escape() .
templates/summary: escape branch/tag/bookmark names in 'Download as zip' links to prevent XSS
On a repository summary page, in the 'Download' section where you can download an archive of the repository at a given revision, the branch/tag names were not correctly escaped.
This means that if an attacker is able to push a branch/tag/bookmark containing HTML/JavaScript in its name, then that code would be evaluated. This is a cross-site scripting (XSS) vulnerability.
Fix the problem by correctly escaping the branch/tag/bookmarks.
Reported by Bob Hogg <wombat@rwhogg.site> (thanks!).
lib: sanitize HTML for all types of README rendering, not only markdown
The repository summary page will display a rendered version of the repository 'readme' based on its file extension. In commit 5746cc3b3fa5, the rendered output was already sanitized when the input was markdown. However, also readmes written in other formats, like ReStructuredText (RST) or plain text could have content that we want sanitized.
Therefore, move the sanitizing one level up so it covers all renderers, for now and the future.
This fixes an XSS issue when a repository readme contains javascript code, which would be executed when the repository summary page is visited by a user.
Reported by Bob Hogg <wombat@rwhogg.site> (thanks!).
cleanup: remove unnecessary (and potentially problematic) use of 'literal'
webhelpers.html.literal (kallithea.lib.helpers.literal) is only needed when the passed string may contain HTML that needs to be interpreted literally. It is unnecessary for plain strings.
Incorrect usage of literal can lead to XSS issues, via a malicious user controlling data which will be rendered in other users' browsers. The data could either be stored previously in the system or be part of a forged URL the victim clicks on.
For example, when a user browses to a forged URL where a repository changeset or branch name contains a javascript snippet, the snippet was executed when printed on the page using 'literal'.
Remaining uses of 'literal' have been reviewed with no apparent problems found.
Reported by Bob Hogg <wombat@rwhogg.site> (thanks!).
pullrequests: prevent XSS in 'Potential Reviewers' list when first and last names cannot be trusted
The user information passed to autocompleteFormatter from select2 is the raw data which might contain HTML markup controlled by the user.
That could cause XSS issues, already when loading a PR page.
To avoid that, make sure autocompleteHighlightMatch always escape user information. That makes the user safe as long as a rogue user isn't selected ...
hg: improve implementations of `successors` and `precursors` properties of Mercurial changesets
* On Mercurial versions supporting it, make the properties return the closest changesets in the obsolescence chain that are in the repository. * On older Mercurial versions, fall back to the previous implementations.
validate-commits can be used to verify a series of commits before submitting/pushing it. It will create a virtualenv and run run-all-cleanup and pytest.
tests: fix assert rewriting in non-test modules like api_base.py
pytest rewrites assert statements in tests so it can print details about the values involved when the assert fails.
Since pytest 3.0.0, this was no longer the case for files/modules that are not discovered as test modules, i.e. starting with 'test_'. Examples are: api/api_base.py models/common.py base.py fixture.py
.coveragerc: fix reporting of coverage to match what is run
Without this change, the HTML report will show all lines specified in run.omit. As they have 0% coverage, this will negatively impact the overall coverage percentage calculated in the report.
Note: at this moment, we have an overall 74% test coverage. Test by installing pytest-cov and running: py.test --cov=kallithea --cov-config=.coveragerc --cov-report html and opening htmlcov/index.html in a browser.
In a fresh virtualenv on the stable branch, pastescript 3.0.0 is installed which depends on paste 3.0.x. Using this virtualenv to upgrade to the default branch, using 'pip install --upgrade -e .' fails because on the default branch, the paste version is restricted with '>= 2.0.3, < 3'. Following error occurs:
pastescript 3.0.0 has requirement Paste>=3.0, but you'll have paste 2.0.3 which is incompatible. ... Traceback (most recent call last): File "<string>", line 1, in <module> File ".../kallithea/kallithea-release/setup.py", line 160, in <module> """, File "/usr/lib64/python2.7/distutils/core.py", line 151, in setup dist.run_commands() File "/usr/lib64/python2.7/distutils/dist.py", line 953, in run_commands self.run_command(cmd) File "/usr/lib64/python2.7/distutils/dist.py", line 972, in run_command cmd_obj.run() File ".../kallithea/venv/kallithea-release/lib/python2.7/site-packages/setuptools/command/develop.py", line 36, in run self.install_for_development() File ".../kallithea/venv/kallithea-release/lib/python2.7/site-packages/setuptools/command/develop.py", line 117, in install_for_development self.run_command('egg_info') File "/usr/lib64/python2.7/distutils/cmd.py", line 326, in run_command self.distribution.run_command(command) File "/usr/lib64/python2.7/distutils/dist.py", line 972, in run_command cmd_obj.run() File ".../kallithea/venv/kallithea-release/lib/python2.7/site-packages/setuptools/command/egg_info.py", line 270, in run ep.require(installer=installer) File ".../kallithea/venv/kallithea-release/lib/python2.7/site-packages/pkg_resources/__init__.py", line 2307, in require items = working_set.resolve(reqs, env, installer) File ".../kallithea/venv/kallithea-release/lib/python2.7/site-packages/pkg_resources/__init__.py", line 854, in resolve raise VersionConflict(dist, req).with_context(dependent_req) pkg_resources.VersionConflict: (Paste 2.0.3 (.../kallithea/venv/kallithea-release/lib/python2.7/site-packages), Requirement.parse('Paste>=3.0'))
The '< 3' restriction is introduced with commit e1ab826131334150b1f003e26de3207c34fc6e67 in January 2017, at which point 2.0.3 was the latest version. Version 3.0.0 was introduced in October 2018.
lib: BaseRepoController: use webob.exc.HTTPNotFound if possible
In the entire code base, the use of 'paste' is very limited. In particular, 'paste.httpexceptions' is only still used in lib/base.py, in three occurrences: - two of them in class BasicAuth which derives from paste.auth.something. Here it probably makes sense to use paste.httpexceptions. - one in BaseRepoController, which has no specific relation to paste. This occurrence can be replaced with webob.exc like in the rest of the code base.
docs: upgrade: make upgrade instructions from version control more explicit
Instruct users to make a note of the orginal and new revision, both to help in any failure analysis, and also to let them realize when the 'hg update' command failed.
auth: move 'active' handling out of the individual auth modules
The 'active' flag in the Kallithea user database is very fundamental and should not be specific to auth modules. Modules should only care about whether the user is active in the external authentication system.
user_activation_state is thus removed, and 'hg.extern_activate.auto' is now consistently checked for all kinds of external authentication.
auth: drop active_from_extern from internal auth API
Modules should never auth a user if the auth source knows the user is inactive. Also, it is too late and unreliable to disable users when they try to log in. There is thus no need for this concept.
Only the crowd module had some traces of actual active_from_extern usage. The 'active' flag for crowd users was fully controlled from crowd. Now, Instead, just let crowd reject authentication of users that are inactive in crowd, and leave the internal Kallithea 'active' flag under admin control.
auth: change get_allowed_ips to be more resilient when operating on a cached default user
Before, random changes to how things are fetched and cached across database sessions could cause get_allowed_ips to fail with:
DetachedInstanceError: Instance <User> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: http://sqlalche.me/e/bhk3)
Instead, just check for user_id, using same pattern as a bit later in same function.
files: use the web browsers built-in js history instead of native.history.js
The history API is available in all web browsers we support.
window.history.pushState is called to register a state that we can go back/forward to. (But contrary to native.history.js, it doesn't do any immediate processing of the state and doesn't actually navigate to it.)
When navigation occurs, we get the popstate event and invoke load_state to actually load the state.
front-end: Move .less files to the front-end folder
The less directory was public - we prefer to have the source file outside the public folder. And we prefer to name the folder after the use of content instead of the primary file type of content.
The last remaining 3rd-party file, mergely.css, was still not used from that location and is just removed.
front-end: Use select2 from node_modules and stop bundling it
select2 3.5.4 was added in 304e83e9bcde ... but the latest npm release in the 3 series is 3.5.1, so we use that one instead. We should probably upgrade to the 4 series.
The select2 images were not in the location the generated css pointed - now we copy them from node_modules to the right location, next to the generated css.
Note: this will drop 190cb30841de "branches: fix performance of branch selectors with many branches - only show the first 200 results" ... but it should no longer be relevant now when we use server side filtering.
15e507047bae introduced select2-bootstrap.css - it is not clear what version was used, but we use the latest 1.4.6 which also is very old.
front-end: Store temporary files in a tmp directory
Avoid mixing temporary files with source files or have them in the generated output. But for now, keep them in the front-end directory where we need write access for node_modules anyway.
front-end: Introduce 'front-end' directory with source files for building the front-end
The top level with package.json is not included when installing with pip, and 'kallithea-cli front-end-build' and npm would thus fail.
Instead, introduce 'kallithea/front-end/'. It is under 'kallithea/' and is thus included when doing pip install, but it is just a source directory and not under "public". Most of the "less" stuff should probably move there. And probably also most things that now are checked in under "public", so a fully populated public front-end directory can be built anywhere, without write access to the Python installation directory.