# # # patch "tracvc/mtn/automate.py" # from [a0a5a0fa90e6b72944f93064851ab6d28cea49f4] # to [0738e3db2fa627c03867f68e054fef5a5e26d404] # # patch "tracvc/mtn/backend.py" # from [5256840759d369117cfb6c04143fe1c35f3192b8] # to [e5f9250a4a43dfdda760e92974292e442a7a0c9d] # ============================================================ --- tracvc/mtn/automate.py a0a5a0fa90e6b72944f93064851ab6d28cea49f4 +++ tracvc/mtn/automate.py 0738e3db2fa627c03867f68e054fef5a5e26d404 @@ -31,7 +31,9 @@ MT_BINARY = "/usr/bin/mtn" # fixme: default, make this configurable TAGS_RULE = re.compile(r"^(?P.*?) (?P[0-9a-f]{40}) (?P.*)\n", re.MULTILINE) -REV_RULE = re.compile('^[0-9a-f]{40}$') +REVID_RULE = re.compile(r'^[0-9a-f]{40}$') +HEAD_RULE = re.compile(r'^h:[a-zA-Z0-9.-]+$') +TAG_RULE = re.compile(r'^t:[a-zA-Z0-9.-]+$') class Connection: """Starts monotone and communicates with it through a pipe.""" @@ -172,9 +174,14 @@ def select(self, selector): """Returns a list of revisions selected by the selector.""" - status, result = self.automate.command("select", selector) - if status == 0: return result.splitlines() - else: return [] + # for now, use a whitelist to prevent SQL injections + if REVID_RULE.match(selector): + return [selector] + elif HEAD_RULE.match(selector) or TAG_RULE.match(selector): + status, result = self.automate.command("select", selector) + if status == 0: + return result.splitlines() + return [] def manifest(self, rev): """ ============================================================ --- tracvc/mtn/backend.py 5256840759d369117cfb6c04143fe1c35f3192b8 +++ tracvc/mtn/backend.py e5f9250a4a43dfdda760e92974292e442a7a0c9d @@ -22,7 +22,7 @@ """ -from trac.versioncontrol.api import Node, Repository, Changeset, IRepositoryConnector, NoSuchNode +from trac.versioncontrol.api import Node, Repository, Changeset, IRepositoryConnector, NoSuchNode, NoSuchChangeset from trac.wiki import IWikiSyntaxProvider from trac.util import shorten_line, escape from trac.core import * @@ -187,11 +187,12 @@ Return a canonical representation of a revision in the repos. 'None' is a valid revision value and represents the youngest revision. """ - if rev: - revs = self.mtn.select(rev) - if revs: - return revs[0] - return self.get_youngest_rev() + if not rev: + return self.get_youngest_rev() + revs = self.mtn.select(rev) + if revs: + return revs[0] + return rev # not normalizable def short_rev(self, rev): """ @@ -304,7 +305,9 @@ class MonotoneChangeset(Changeset): def __init__(self, mtn, rev): - self.certs = mtn.certs(rev) + self.certs = rev and mtn.certs(rev) + if not self.certs: + raise NoSuchChangeset(rev) # always pick the first self.messages = self.certs.get('changelog', ['-'])