diff -u viewvc-1.1.5/debian/changelog viewvc-1.1.5/debian/changelog --- viewvc-1.1.5/debian/changelog +++ viewvc-1.1.5/debian/changelog @@ -1,3 +1,41 @@ +viewvc (1.1.5-1.1+squeeze2build0.12.04.1) precise-security; urgency=low + + * fake sync from Debian + + -- Marc Deslauriers Wed, 21 Nov 2012 09:47:06 -0500 + +viewvc (1.1.5-1.1+squeeze2) stable-security; urgency=high + + * Non-maintainer upload. + * CVE-2012-4533: Fix XSS in commit message view. Found and patch provided + by Nicolás Alvarez (closes: #691062). + + -- Thijs Kinkhorst Tue, 23 Oct 2012 10:30:11 +0200 + +viewvc (1.1.5-1.1+squeeze1) stable-security; urgency=high + + * Non-maintainer upload. + + [ gregor herrmann ] + * [SECURITY] Fix "CVE-2012-3356 / CVE-2012-3357": + - CVE-2012-3356: * security fix: complete authz support for remote SVN views + - CVE-2012-3357: * security fix: log msg leak in SVN revision view with + unreadable copy source + Add patches "CVE-2012-3356" and "CVE-2012-3357", taken from upstream svn. + (Closes: #679069) + * Fix "viewvc runs extremely slowly (~15s per page)": + backport upstream commit r2471 as new patch compression-content-length: + don't set Content-Length when compression is used. + (Closes: #636805) + + [ Ben Hutchings ] + * view_query: No longer allow an undocumented URL parameter to + override the admin-declared SQL row limit, which could result + in excessive CPU usage and memory consumption (CVE-2009-5024) + (Closes: #671482) + + -- Thijs Kinkhorst Sun, 14 Oct 2012 20:12:07 +0000 + viewvc (1.1.5-1.1) unstable; urgency=low * Non-maintainer upload. diff -u viewvc-1.1.5/debian/patches/series viewvc-1.1.5/debian/patches/series --- viewvc-1.1.5/debian/patches/series +++ viewvc-1.1.5/debian/patches/series @@ -5,0 +6,6 @@ +rev2547 +rev2551 +compression-content-length +CVE-2012-3357 +CVE-2012-3356 +CVE-2012-4533 only in patch2: unchanged: --- viewvc-1.1.5.orig/debian/patches/CVE-2012-3356 +++ viewvc-1.1.5/debian/patches/CVE-2012-3356 @@ -0,0 +1,368 @@ +Bugs-Debian: http://bugs.debian.org/679069 +Description: fix CVE-2012-3356 +Last-Update: 2012-09-05 +Origin: upstream svn, cf. details below +Comment: this is the commit from upstream, with the path adjusted and quilt + refresh'd plus heavy manual backporting to the old version in Debian + because 5/13 hunks failed + +http://viewvc.tigris.org/source/browse/viewvc?view=rev&revision=2755 + +Author cmpilato +Date 2012-06-13 14:57:38-0700 (2 months, 3 weeks ago) +Log message + +For issue #353, check path access in the code that backs the revision +log view for remote SVN repositories. + +* lib/vclib/svn/svn_ra.py + (LogCollector.__init__): Add 'access_check_func' parameter, stashed + away as a member variable. Initialize 'done' variable to False. + (LogCollector.add_log): Use access_check_func() to test for access. + If access is denied, set 'done' to True and ignore future + invocations. + (RemoteSubversionRepository.itemlog): Add _access_checker inner + function, passed as a callback to LogCollector.__init__(). + +http://viewvc.tigris.org/source/browse/viewvc?view=rev&revision=2756 + +Author cmpilato +Date 2012-06-14 19:11:12-0700 (2 months, 3 weeks ago) +Log message + +For issue #353 ("Remote Subversion repositories not fully honoring +authz rules"): + +* lib/vclib/svn/svn_ra.py + (RemoteSubversionRepository.get_locations): Check authz on the + result of a get_locations lookup (being sure to use the original + path in the error message, if any). + +http://viewvc.tigris.org/source/browse/viewvc?view=rev&revision=2757 + +Author cmpilato +Date 2012-06-14 19:17:55-0700 (2 months, 3 weeks ago) +Log message + +For issue #353, check path access in the annotation view code for +remote SVN repositories. + +* lib/vclib/svn/svn_ra.py + (RemoteSubversionRepository.annotate): Examine revision logs to + determine the legal annotation range we are allowed to request, + and use the revinfo cache to strip sensitive author/data info from + the annotation data. + +http://viewvc.tigris.org/source/browse/viewvc?view=rev&revision=2759 + +Author cmpilato +Date 2012-06-15 16:07:22-0700 (2 months, 2 weeks ago) +Log message + +* lib/vclib/svn/svn_ra.py + (LogCollector.add_log): Allow the access_check_func to be None (and + avoid trying to call it in that case). + +http://viewvc.tigris.org/source/browse/viewvc?view=rev&revision=2760 + +Author cmpilato +Date 2012-06-15 16:34:01-0700 (2 months, 2 weeks ago) +Log message + +Finish, I believe, issue #353 ("Remote Subversion repositories not +fully honoring authz rules"). In doing so, this makes the svn_ra +module the most accurate it has ever been, hopefully without too +terribly much extra cost. + +* lib/vclib/svn/svn_ra.py + (RemoteSubversionRepository.listdir): No longer check access here. + That's handled down in _get_dirents() now. + (RemoteSubversionRepository.dirlogs): Don't check dirent access here. + Just ensure that the returned entries are those which are + represented in the (filtered) set return from _get_dirents(). + Also, replace the revision metadata stored on each dirent with + values from the revinfo cache (which does authz sanitizing). + (RemoteSubversionRepository._get_dirents): Do authz-checking of + dirents here, and use _get_last_history_rev() to populate a useful + created_rev. + (RemoteSubversionRepository._get_last_history_rev): Because + Subversion's RA layer doesn't consider copy events when + determining an item's last-committed-rev, compensate for this with + a bounded log operation which does. This brings created-rev + parity with the svn_repos module (at the cost of the extra RA + work, but with the benefit of, you know, accuracy. + (RemoteSubversionRepository._revinfo_raw): Leave a TODO about a + future optimization. + (RemoteSubversionRepository._log_cb): Set found_unreadable when + sanitizing an unreadable copyfrom path, too. + (RemoteSubversionRepository.created_rev): Now just a thin wrapper + around the beefed-up _get_last_history_rev() function. + +http://viewvc.tigris.org/source/browse/viewvc?view=rev&revision=2761 + +Author cmpilato +Date 2012-06-15 17:03:57-0700 (2 months, 2 weeks ago) +Log message + +Merge the fixes for issue #353 ("Remote Subversion repositories not +fully honoring authz rules") from trunk. This merges r2755, r2756, +r2757, r2759, and r2760, which see for detail log revision information. + +--- a/lib/vclib/svn/svn_ra.py ++++ b/lib/vclib/svn/svn_ra.py +@@ -56,9 +56,8 @@ + + + class LogCollector: +- ### TODO: Make this thing authz-aware + +- def __init__(self, path, show_all_logs, lockinfo): ++ def __init__(self, path, show_all_logs, lockinfo, access_check_func): + # This class uses leading slashes for paths internally + if not path: + self.path = '/' +@@ -67,8 +66,12 @@ + self.logs = [] + self.show_all_logs = show_all_logs + self.lockinfo = lockinfo ++ self.access_check_func = access_check_func ++ self.done = False + + def add_log(self, paths, revision, author, date, message, pool): ++ if self.done: ++ return + # Changed paths have leading slashes + changed_paths = paths.keys() + changed_paths.sort(lambda a, b: _compare_paths(a, b)) +@@ -88,9 +91,13 @@ + if change.copyfrom_path: + this_path = change.copyfrom_path + self.path[len(changed_path):] + if self.show_all_logs or this_path: +- entry = Revision(revision, _datestr_to_date(date), author, message, None, +- self.lockinfo, self.path[1:], None, None) +- self.logs.append(entry) ++ if self.access_check_func is None \ ++ or self.access_check_func(self.path[1:], revision): ++ entry = Revision(revision, _datestr_to_date(date), author, message, None, ++ self.lockinfo, self.path[1:], None, None) ++ self.logs.append(entry) ++ else: ++ self.done = True + if this_path: + self.path = this_path + +@@ -229,7 +236,7 @@ + if self.itemtype(path_parts, rev) != vclib.DIR: # does auth-check + raise vclib.Error("Path '%s' is not a directory." % path) + rev = self._getrev(rev) +- entries = [ ] ++ entries = [] + dirents, locks = self._get_dirents(path, rev) + for name in dirents.keys(): + entry = dirents[name] +@@ -237,8 +244,9 @@ + kind = vclib.DIR + elif entry.kind == core.svn_node_file: + kind = vclib.FILE +- if vclib.check_path_access(self, path_parts + [name], kind, rev): +- entries.append(vclib.DirEntry(name, kind)) ++ else: ++ kind = None ++ entries.append(vclib.DirEntry(name, kind)) + return entries + + def dirlogs(self, path_parts, rev, entries, options): +@@ -249,9 +257,11 @@ + dirents, locks = self._get_dirents(path, rev) + for entry in entries: + entry_path_parts = path_parts + [entry.name] +- if not vclib.check_path_access(self, entry_path_parts, entry.kind, rev): ++ dirent = dirents.get(entry.name, None) ++ # dirents is authz-sanitized, so ensure the entry is found therein. ++ if dirent is None: + continue +- dirent = dirents[entry.name] ++ # Get authz-sanitized revision metadata. + entry.date, entry.author, entry.log, changes = \ + self.revinfo(dirent.created_rev) + entry.rev = str(dirent.created_rev) +@@ -275,9 +285,14 @@ + if locks.has_key(basename): + lockinfo = locks[basename].owner + ++ def _access_checker(check_path, check_rev): ++ return vclib.check_path_access(self, _path_parts(check_path), ++ path_type, check_rev) ++ + # It's okay if we're told to not show all logs on a file -- all + # the revisions should match correctly anyway. +- lc = LogCollector(path, options.get('svn_show_all_dir_logs', 0), lockinfo) ++ lc = LogCollector(path, options.get('svn_show_all_dir_logs', 0), ++ lockinfo, _access_checker) + + cross_copies = options.get('svn_cross_copies', 0) + log_limit = 0 +@@ -290,6 +305,10 @@ + revs.sort() + prev = None + for rev in revs: ++ # Swap out revision info with stuff from the cache (which is ++ # authz-sanitized). ++ rev.date, rev.author, rev.log, revprops, changes \ ++ = self.revinfo(rev.number) + rev.prev = prev + prev = rev + revs.reverse() +@@ -316,6 +335,14 @@ + rev = self._getrev(rev) + url = self._geturl(path) + ++ # Examine logs for the file to determine the oldest revision we are ++ # permitted to see. ++ revs = self.itemlog(path_parts, rev, vclib.SORTBY_REV, 0, 0, {}) ++ oldest_rev = revs[-1].number ++ ++ # Now calculate the annotation data. Note that we'll not ++ # inherently trust the provided author and date, because authz ++ # rules might necessitate that we strip that information out. + blame_data = [] + + def _blame_cb(line_no, revision, author, date, +@@ -323,10 +350,14 @@ + prev_rev = None + if revision > 1: + prev_rev = revision - 1 ++ if revision >= 0: ++ date, author, msg, revprops, changes = self.revinfo(revision) ++ else: ++ date = author = None + blame_data.append(vclib.Annotation(line, line_no+1, revision, prev_rev, + author, None)) + +- client.svn_client_blame(url, _rev2optrev(1), _rev2optrev(rev), ++ client.svn_client_blame(url, _rev2optrev(oldest_rev), _rev2optrev(rev), + _blame_cb, self.ctx) + + return blame_data, rev +@@ -392,32 +423,73 @@ + + def _get_dirents(self, path, rev): + """Return a 2-type of dirents and locks, possibly reading/writing +- from a local cache of that information.""" ++ from a local cache of that information. This functions performs ++ authz checks, stripping out unreadable dirents.""" + + dir_url = self._geturl(path) + if path: + key = str(rev) + '/' + path + else: + key = str(rev) ++ ++ # Ensure that the cache gets filled... + dirents_locks = self._dirent_cache.get(key) + if not dirents_locks: +- dirents, locks = list_directory(dir_url, _rev2optrev(rev), +- _rev2optrev(rev), 0, self.ctx) ++ tmp_dirents, locks = list_directory(dir_url, _rev2optrev(rev), ++ _rev2optrev(rev), 0, self.ctx) ++ dirents = {} ++ for name, dirent in tmp_dirents.items(): ++ dirent_parts = _path_parts(path) + [name] ++ kind = dirent.kind ++ if (kind == core.svn_node_dir or kind == core.svn_node_file) \ ++ and vclib.check_path_access(self, dirent_parts, ++ kind == core.svn_node_dir \ ++ and vclib.DIR or vclib.FILE, rev): ++ dirent.created_rev = self._get_last_history_rev(dirent_parts, rev) ++ dirents[name] = dirent + dirents_locks = [dirents, locks] + self._dirent_cache[key] = dirents_locks ++ ++ # ...then return the goodies from the cache. + return dirents_locks[0], dirents_locks[1] + + def _get_last_history_rev(self, path_parts, rev): ++ """Return the last interesting revision equal to or older than REV ++ in the history of PATH_PARTS.""" ++ ++ path = self._getpath(path_parts) + url = self._geturl(self._getpath(path_parts)) + optrev = _rev2optrev(rev) ++ ++ # Get the last-changed-rev. + revisions = [] + def _info_cb(path, info, pool, retval=revisions): + revisions.append(info.last_changed_rev) + client.svn_client_info(url, optrev, optrev, _info_cb, 0, self.ctx) +- return revisions[0] ++ last_changed_rev = revisions[0] ++ ++ # Now, this object might not have been directly edited since the ++ # last-changed-rev, but it might have been the child of a copy. ++ # To determine this, we'll run a potentially no-op log between ++ # LAST_CHANGED_REV and REV. ++ lc = LogCollector(path, 1, None, None) ++ client_log(url, optrev, _rev2optrev(last_changed_rev), 1, 0, ++ lc.add_log, self.ctx) ++ revs = lc.logs ++ if revs: ++ revs.sort() ++ return revs[0].number ++ else: ++ return last_changed_rev + + def _revinfo_raw(self, rev): + # return 5-tuple (date, author, message, changes) ++ ++ ### TODO: This function and its related cache would benefit from ++ ### optimizations such as what the matching svn_repos functions ++ ### have, where the 'changes' information is only fully ++ ### calculated/authz-sanitized when the caller actually needs it. ++ + optrev = _rev2optrev(rev) + revs = [] + +@@ -457,10 +529,11 @@ + if vclib.check_path_access(self, parts, vclib.FILE, revision): + if is_copy and base_path and (base_path != path): + parts = _path_parts(base_path) +- if vclib.check_path_access(self, parts, vclib.FILE, base_rev): ++ if not vclib.check_path_access(self, parts, vclib.FILE, base_rev): + is_copy = 0 + base_path = None + base_rev = None ++ found_unreadable = 1 + changes.append(SVNChangedPath(path, revision, pathtype, base_path, + base_rev, action, is_copy, 0, 0)) + found_readable = 1 +@@ -495,23 +568,15 @@ + old_path = results[old_rev] + except KeyError: + raise vclib.ItemNotFound(path) +- +- return _cleanup_path(old_path) ++ old_path = _cleanup_path(old_path) ++ old_path_parts = _path_parts(old_path) ++ # Check access (lying about path types) ++ if not vclib.check_path_access(self, old_path_parts, vclib.FILE, old_rev): ++ raise vclib.ItemNotFound(path) ++ return old_path + + def created_rev(self, path, rev): +- # NOTE: We can't use svn_client_propget here because the +- # interfaces in that layer strip out the properties not meant for +- # human consumption (such as svn:entry:committed-rev, which we are +- # using here to get the created revision of PATH@REV). +- kind = ra.svn_ra_check_path(self.ra_session, path, rev) +- if kind == core.svn_node_none: +- raise vclib.ItemNotFound(_path_parts(path)) +- elif kind == core.svn_node_dir: +- props = get_directory_props(self.ra_session, path, rev) +- elif kind == core.svn_node_file: +- fetched_rev, props = ra.svn_ra_get_file(self.ra_session, path, rev, None) +- return int(props.get(core.SVN_PROP_ENTRY_COMMITTED_REV, +- SVN_INVALID_REVNUM)) ++ return self._get_last_history_rev(_path_parts(path), rev) + + def last_rev(self, path, peg_revision, limit_revision=None): + """Given PATH, known to exist in PEG_REVISION, find the youngest only in patch2: unchanged: --- viewvc-1.1.5.orig/debian/patches/rev2547 +++ viewvc-1.1.5/debian/patches/rev2547 @@ -0,0 +1,175 @@ +------------------------------------------------------------------------ +r2547 | cmpilato | 2011-04-19 21:40:04 +0100 (Tue, 19 Apr 2011) | 30 lines + +Try to make some sense of the various CVSdb-related limitation +mechanisms, namely by removing the largely redundant "global" limit +and allowing the per-query row limit (which already exist, too) to do +its work. + +While here, remove a poorly conceived (but thankfully unhighlighted) +mechanism for overriding the administrative limit on database rows +which was accessible via URL CGI params. + +* lib/viewvc.py + (_legal_params): Remove 'limit' as a legal parameter. + (view_query): No longer allow an undocumented URL parameter to + override the admin-declared SQL row limit. That should have never + been allowed! + +* lib/cvsdb.py + (CheckinDatabase.__init__): Remove 'row_limit' parameter and + associated self._row_limit member. + (CheckinDatabase.CreateSQLQueryString): No longer fuss with + self._row_limit. Let the individual query carry the row limit. + (ConnectDatabase): Update call to CheckinDatabase(). + +* lib/query.py + (form_to_cvsdb_query): Now accept 'cfg' parameter, and set the + query's row limit from the configured defaults. + (run_query): Update call to form_to_cvsdb_query(). + +* docs/url-reference.html + Remove reference to the 'limit' parameter. + +------------------------------------------------------------------------ +--- viewvc-1.1.5.orig/docs/url-reference.html ++++ viewvc-1.1.5/docs/url-reference.html +@@ -1193,13 +1193,6 @@ + page + + +- limit=LIMIT +- optional +- maximum number of file-revisions to process during a +- query. Default is value of row_limit configuration +- option +- +- + limit_changes=LIMIT_CHANGES + optional + maximum number of files to list per commit in query +--- viewvc-1.1.5.orig/lib/cvsdb.py ++++ viewvc-1.1.5/lib/cvsdb.py +@@ -38,13 +38,12 @@ + ## complient database interface + + class CheckinDatabase: +- def __init__(self, host, port, user, passwd, database, row_limit): ++ def __init__(self, host, port, user, passwd, database): + self._host = host + self._port = port + self._user = user + self._passwd = passwd + self._database = database +- self._row_limit = row_limit + self._version = None + + ## database lookup caches +@@ -444,13 +443,11 @@ + conditions = string.join(joinConds + condList, " AND ") + conditions = conditions and "WHERE %s" % conditions + +- ## limit the number of rows requested or we could really slam +- ## a server with a large database ++ ## apply the query's row limit, if any (so we avoid really ++ ## slamming a server with a large database) + limit = "" + if query.limit: + limit = "LIMIT %s" % (str(query.limit)) +- elif self._row_limit: +- limit = "LIMIT %s" % (str(self._row_limit)) + + sql = "SELECT %s.* FROM %s %s %s %s" \ + % (commits_table, tables, conditions, order_by, limit) +@@ -769,8 +766,8 @@ + self.data = data + self.match = match + +-## CheckinDatabaseQueryData is a object which contains the search parameters +-## for a query to the CheckinDatabase ++## CheckinDatabaseQuery is an object which contains the search ++## parameters for a query to the Checkin Database + class CheckinDatabaseQuery: + def __init__(self): + ## sorting +@@ -861,7 +858,7 @@ + user = cfg.cvsdb.user + passwd = cfg.cvsdb.passwd + db = CheckinDatabase(cfg.cvsdb.host, cfg.cvsdb.port, user, passwd, +- cfg.cvsdb.database_name, cfg.cvsdb.row_limit) ++ cfg.cvsdb.database_name) + db.Connect() + return db + +--- viewvc-1.1.5.orig/lib/viewvc.py ++++ viewvc-1.1.5/lib/viewvc.py +@@ -706,7 +706,6 @@ + 'mindate' : _re_validate_datetime, + 'maxdate' : _re_validate_datetime, + 'format' : _re_validate_alpha, +- 'limit' : _re_validate_number, + + # for redirect_pathrev + 'orig_path' : None, +@@ -3959,7 +3958,6 @@ + mindate = request.query_dict.get('mindate', '') + maxdate = request.query_dict.get('maxdate', '') + format = request.query_dict.get('format') +- limit = int(request.query_dict.get('limit', 0)) + limit_changes = int(request.query_dict.get('limit_changes', + cfg.options.limit_changes)) + +@@ -4029,16 +4027,17 @@ + query.SetFromDateObject(mindate) + if maxdate is not None: + query.SetToDateObject(maxdate) +- if limit: +- query.SetLimit(limit) +- elif format == 'rss': ++ ++ # Set the admin-defined (via configuration) row limits. This is to avoid ++ # slamming the database server with a monster query. ++ if format == 'rss': + query.SetLimit(cfg.cvsdb.rss_row_limit) ++ else: ++ query.SetLimit(cfg.cvsdb.row_limit) + + # run the query + db.RunQuery(query) + +- sql = request.server.escape(db.CreateSQLQueryString(query)) +- + # gather commits + commits = [] + plus_count = 0 +@@ -4120,7 +4119,7 @@ + + data = common_template_data(request) + data.merge(ezt.TemplateData({ +- 'sql': sql, ++ 'sql': request.server.escape(db.CreateSQLQueryString(query)), + 'english_query': english_query(request), + 'queryform_href': request.get_url(view_func=view_queryform, escape=1), + 'backout_href': backout_href, +--- viewvc-1.1.5.orig/lib/query.py ++++ viewvc-1.1.5/lib/query.py +@@ -217,8 +217,9 @@ + else: + return "exact" + +-def form_to_cvsdb_query(form_data): ++def form_to_cvsdb_query(cfg, form_data): + query = cvsdb.CreateCheckinQuery() ++ query.SetLimit(cfg.cvsdb.row_limit) + + if form_data.repository: + for cmd, str in listparse_string(form_data.repository): +@@ -380,7 +381,7 @@ + return ob + + def run_query(server, cfg, form_data, viewvc_link): +- query = form_to_cvsdb_query(form_data) ++ query = form_to_cvsdb_query(cfg, form_data) + db = cvsdb.ConnectDatabaseReadOnly(cfg) + db.RunQuery(query) + only in patch2: unchanged: --- viewvc-1.1.5.orig/debian/patches/rev2551 +++ viewvc-1.1.5/debian/patches/rev2551 @@ -0,0 +1,308 @@ +------------------------------------------------------------------------ +r2551 | cmpilato | 2011-04-20 15:50:40 +0100 (Wed, 20 Apr 2011) | 41 lines + +Fix (to the degree that I believe is reasonable at this time) issue +#433 ("queries return only partial results"). When a database query +is artificially limited by the 'row_limit' setting, inform the user +that the returned data is incomplete. + +* lib/cvsdb.py + (CheckinDatabase.CreateSQLQueryString): Add 'detect_leftover' + parameter, used internally to check for a reached query limit. + (CheckinDatabase.RunQuery): Update call to CreateSQLQueryString(), + and check for leftover query response rows. If any are found, set + the appropriate flag on the query object. + (CheckinDatabaseQuery.__init__): Set initial values for new + 'executed' and 'limit_reached' members. + (CheckinDatabaseQuery.SetExecuted, + CheckinDatabaseQuery.SetLimitReached, + CheckinDatabaseQuery.GetLimitReached, + CheckinDatabaseQuery.GetCommitList): New functions. + +* lib/viewvc.py + (view_query): Use query.GetCommitList() now instead of poking into + the query object directly. Also, check query.GetLimitReached(), + reporting the findings through the data dictionary (via a new + 'row_limit_reached' item) to the templates. + +* lib/query.py + (run_query): Use query.GetCommitList() now instead of poking into + the query object directly. Now return a 2-tuple of commits and a + limit-reached flag. + (main): Update expectations of run_query() call. Populate + 'row_limit_reached' data dictionary item. + +* templates/query_results.ezt, +* templates/query.ezt + Display a warning if the query results are incomplete. + +* templates/docroot/styles.css + (.vc_warning): New style definition. + +* docs/template-authoring-guide.html + Document the new 'row_limit_reached' template item. + +------------------------------------------------------------------------ +--- viewvc-1.1.5.orig/docs/template-authoring-guide.html ++++ viewvc-1.1.5/docs/template-authoring-guide.html +@@ -1822,6 +1822,14 @@ + date, author, and file. + + ++ row_limit_reached ++ Boolean ++ Indicates whether the internal database row limit threshold (set ++ via the cvsdb.row_limit ++ and cvsdb.rss_row_limit configuration options) was ++ reached by the query. ++ ++ + show_branch + Boolean + Indicates whether or not to list branch names in the results. True +--- viewvc-1.1.5.orig/templates/query_results.ezt ++++ viewvc-1.1.5/templates/query_results.ezt +@@ -7,6 +7,15 @@ + +

[english_query]

+ [# ] ++[if-any row_limit_reached] ++

WARNING: These query results have been ++ artificially limited by an administrative threshold value and do ++ not represent the entirety of the data set which matches ++ the query. Consider modifying your ++ query to be more specific, using your version control tool's ++ query capabilities, or asking your adminstrator to raise the ++ database response size threshold.

++[end] +

Modify query

+

Show commands which could be used to back out these changes

+ +--- viewvc-1.1.5.orig/templates/query.ezt ++++ viewvc-1.1.5/templates/query.ezt +@@ -158,7 +158,15 @@ + [is query "skipped"] + [else] +

[num_commits] matches found.

+- ++[if-any row_limit_reached] ++

WARNING: These query results have been ++ artificially limited by an administrative threshold value and do ++ not represent the entirety of the data set which matches ++ the query. Consider modifying your query to be more specific, ++ using your version control tool's query capabilities, or asking ++ your adminstrator to raise the database response size ++ threshold.

++[end] + [if-any commits] + + +--- viewvc-1.1.5.orig/templates/docroot/styles.css ++++ viewvc-1.1.5/templates/docroot/styles.css +@@ -272,3 +272,14 @@ + .vc_query_form { + background-color: #e6e6e6; + } ++ ++ ++/*** Warning! ***/ ++.vc_warning { ++ border-width: 1px 2px 2px 2px; ++ border-color: black; ++ border-style: solid; ++ background-color: red; ++ color: white; ++ padding: 0.5em; ++} +--- viewvc-1.1.5.orig/lib/cvsdb.py ++++ viewvc-1.1.5/lib/cvsdb.py +@@ -362,7 +362,7 @@ + + return "(%s)" % (string.join(sqlList, " OR ")) + +- def CreateSQLQueryString(self, query): ++ def CreateSQLQueryString(self, query, detect_leftover=0): + commits_table = self._version >= 1 and 'commits' or 'checkins' + tableList = [(commits_table, None)] + condList = [] +@@ -447,7 +447,10 @@ + ## slamming a server with a large database) + limit = "" + if query.limit: +- limit = "LIMIT %s" % (str(query.limit)) ++ if detect_leftover: ++ limit = "LIMIT %s" % (str(query.limit + 1)) ++ else: ++ limit = "LIMIT %s" % (str(query.limit)) + + sql = "SELECT %s.* FROM %s %s %s %s" \ + % (commits_table, tables, conditions, order_by, limit) +@@ -455,14 +458,20 @@ + return sql + + def RunQuery(self, query): +- sql = self.CreateSQLQueryString(query) ++ sql = self.CreateSQLQueryString(query, 1) + cursor = self.db.cursor() + cursor.execute(sql) ++ query.SetExecuted() ++ row_count = 0 + + while 1: + row = cursor.fetchone() + if not row: + break ++ row_count = row_count + 1 ++ if query.limit and (row_count > query.limit): ++ query.SetLimitReached() ++ break + + (dbType, dbCI_When, dbAuthorID, dbRepositoryID, dbDirID, + dbFileID, dbRevision, dbStickyTag, dbBranchID, dbAddedLines, +@@ -767,7 +776,8 @@ + self.match = match + + ## CheckinDatabaseQuery is an object which contains the search +-## parameters for a query to the Checkin Database ++## parameters for a query to the Checkin Database and -- after the ++## query is executed -- the data returned by the query. + class CheckinDatabaseQuery: + def __init__(self): + ## sorting +@@ -787,7 +797,8 @@ + + ## limit on number of rows to return + self.limit = None +- ++ self.limit_reached = 0 ++ + ## list of commits -- filled in by CVS query + self.commit_list = [] + +@@ -795,6 +806,9 @@ + ## are added + self.commit_cb = None + ++ ## has this query been run? ++ self.executed = 0 ++ + def SetRepository(self, repository, match = "exact"): + self.repository_list.append(QueryEntry(repository, match)) + +@@ -840,6 +854,20 @@ + def AddCommit(self, commit): + self.commit_list.append(commit) + ++ def SetExecuted(self): ++ self.executed = 1 ++ ++ def SetLimitReached(self): ++ self.limit_reached = 1 ++ ++ def GetLimitReached(self): ++ assert self.executed ++ return self.limit_reached ++ ++ def GetCommitList(self): ++ assert self.executed ++ return self.commit_list ++ + + ## + ## entrypoints +--- viewvc-1.1.5.orig/lib/viewvc.py ++++ viewvc-1.1.5/lib/viewvc.py +@@ -4037,20 +4037,22 @@ + + # run the query + db.RunQuery(query) +- ++ commit_list = query.GetCommitList() ++ row_limit_reached = query.GetLimitReached() ++ + # gather commits + commits = [] + plus_count = 0 + minus_count = 0 + mod_time = -1 +- if query.commit_list: ++ if commit_list: + files = [] + limited_files = 0 +- current_desc = query.commit_list[0].GetDescriptionID() +- current_rev = query.commit_list[0].GetRevision() ++ current_desc = commit_list[0].GetDescriptionID() ++ current_rev = commit_list[0].GetRevision() + dir_strip = _path_join(repos_dir) + +- for commit in query.commit_list: ++ for commit in commit_list: + commit_desc = commit.GetDescriptionID() + commit_rev = commit.GetRevision() + +@@ -4128,6 +4130,7 @@ + 'show_branch': show_branch, + 'querysort': querysort, + 'commits': commits, ++ 'row_limit_reached' : ezt.boolean(row_limit_reached), + 'limit_changes': limit_changes, + 'limit_changes_href': limit_changes_href, + 'rss_link_href': request.get_url(view_func=view_query, +--- viewvc-1.1.5.orig/lib/query.py ++++ viewvc-1.1.5/lib/query.py +@@ -385,8 +385,11 @@ + db = cvsdb.ConnectDatabaseReadOnly(cfg) + db.RunQuery(query) + +- if not query.commit_list: +- return [ ] ++ commit_list = query.GetCommitList() ++ if not commit_list: ++ return [ ], 0 ++ ++ row_limit_reached = query.GetLimitReached() + + commits = [ ] + files = [ ] +@@ -397,8 +400,8 @@ + for key, value in rootitems: + cvsroots[cvsdb.CleanRepository(value)] = key + +- current_desc = query.commit_list[0].GetDescription() +- for commit in query.commit_list: ++ current_desc = commit_list[0].GetDescription() ++ for commit in commit_list: + desc = commit.GetDescription() + if current_desc == desc: + files.append(commit) +@@ -421,7 +424,7 @@ + return len(commit.files) > 0 + commits = filter(_only_with_files, commits) + +- return commits ++ return commits, row_limit_reached + + def main(server, cfg, viewvc_link): + try: +@@ -430,10 +433,12 @@ + form_data = FormData(form) + + if form_data.valid: +- commits = run_query(server, cfg, form_data, viewvc_link) ++ commits, row_limit_reached = run_query(server, cfg, ++ form_data, viewvc_link) + query = None + else: + commits = [ ] ++ row_limit_reached = 0 + query = 'skipped' + + data = ezt.TemplateData({ +@@ -453,6 +458,7 @@ + 'date' : form_data.date, + + 'query' : query, ++ 'row_limit_reached' : ezt.boolean(row_limit_reached), + 'commits' : commits, + 'num_commits' : len(commits), + 'rss_href' : None, only in patch2: unchanged: --- viewvc-1.1.5.orig/debian/patches/CVE-2012-4533 +++ viewvc-1.1.5/debian/patches/CVE-2012-4533 @@ -0,0 +1,13 @@ +Index: viewvc-1.1.5/lib/viewvc.py +=================================================================== +--- viewvc-1.1.5.orig/lib/viewvc.py 2012-10-20 17:50:09.000000000 -0300 ++++ viewvc-1.1.5/lib/viewvc.py 2012-10-20 17:51:24.000000000 -0300 +@@ -2819,7 +2819,7 @@ + return _item(type='header', + line_info_left=match.group(1), + line_info_right=match.group(2), +- line_info_extra=match.group(3)) ++ line_info_extra=self._format_text(match.group(3))) + + if line[0] == '\\': + # \ No newline at end of file only in patch2: unchanged: --- viewvc-1.1.5.orig/debian/patches/CVE-2012-3357 +++ viewvc-1.1.5/debian/patches/CVE-2012-3357 @@ -0,0 +1,52 @@ +Bugs-Debian: http://bugs.debian.org/679069 +Description: fix CVE-2012-3357 +Last-Update: 2012-09-05 +Origin: upstream svn, cf. details below +Comment: this is the original commit from upstream, with only the path + adjusted and quilt refresh'd + +http://viewvc.tigris.org/source/browse/viewvc?view=rev&revision=2758 + +Author cmpilato +Date 2012-06-15 16:05:47-0700 (2 months, 2 weeks ago) +Log message + +Fix a security issue: When a readable path is copied from an +unreadable one, Subversion will obscure the fact that the operation +was a copy (by removing copyfrom info) and will deem the log message +for the revision in which the copy occurred to be unreadable. ViewVC +was only doing the former bit; now it does the latter, too. + +* lib/vclib/svn/svn_repos.py + (LocalSubversionRepository._get_changed_paths): Set found_unreadable + when we have to hide a copyfrom path, too. + +http://viewvc.tigris.org/source/browse/viewvc?view=rev&revision=2762 + +Author cmpilato +Date 2012-06-15 17:25:33-0700 (2 months, 2 weeks ago) +Log message + +Merge from trunk r2758, whose log message read like so: + + Fix a security issue: When a readable path is copied from an + unreadable one, Subversion will obscure the fact that the operation + was a copy (by removing copyfrom info) and will deem the log message + for the revision in which the copy occurred to be unreadable. ViewVC + was only doing the former bit; now it does the latter, too. + + * lib/vclib/svn/svn_repos.py + (LocalSubversionRepository._get_changed_paths): Set found_unreadable + when we have to hide a copyfrom path, too. + + +--- a/lib/vclib/svn/svn_repos.py ++++ b/lib/vclib/svn/svn_repos.py +@@ -648,6 +648,7 @@ + is_copy = 0 + change.base_path = None + change.base_rev = None ++ found_unreadable = 1 + changedpaths[path] = SVNChangedPath(path, rev, pathtype, + change.base_path, + change.base_rev, action, only in patch2: unchanged: --- viewvc-1.1.5.orig/debian/patches/compression-content-length +++ viewvc-1.1.5/debian/patches/compression-content-length @@ -0,0 +1,54 @@ +Description: Fix issue #467 (I think...) by not claiming a particular content + length when compression would skew that. + . + * lib/viewvc.py + (get_writeready_server_file): Add optional 'content_length' + parameter, and the code to handle it. + (view_doc): Update call to get_writeready_server_file(). +Origin: upstream, svn r2471 +Bug: http://viewvc.tigris.org/issues/show_bug.cgi?id=467 +Bug-Debian: http://bugs.debian.org/636805 +Author: C. Michael Pilato +Reviewed-by: gregor herrmann +Last-Update: 2012-08-07 +Applied-Upstream: yup + +--- a/lib/viewvc.py ++++ b/lib/viewvc.py +@@ -896,13 +896,17 @@ + + return template + +-def get_writeready_server_file(request, content_type=None): ++def get_writeready_server_file(request, content_type=None, content_length=None): + """Return a file handle to a response body stream, after outputting + any queued special headers (on REQUEST.server) and (optionally) a + 'Content-Type' header whose value is CONTENT_TYPE. After this is +- called, it is too late to add new headers to the response.""" ++ called, it is too late to add new headers to the response. ++ If CONTENT_LENGTH is provided and compression is not in use, also ++ generate a 'Content-Length' header for this response.""" + if request.gzip_compress_level: + request.server.addheader('Content-Encoding', 'gzip') ++ elif content_length is not None: ++ request.server.addheader('Content-Length', content_length) + if content_type: + request.server.header(content_type) + else: +@@ -2699,7 +2703,6 @@ + raise debug.ViewVCException('Static file "%s" not available (%s)' + % (document, str(v)), '404 Not Found') + +- request.server.addheader('Content-Length', content_length) + if document[-3:] == 'png': + mime_type = 'image/png' + elif document[-3:] == 'jpg': +@@ -2710,7 +2713,7 @@ + mime_type = 'text/css' + else: # assume HTML: + mime_type = None +- copy_stream(fp, get_writeready_server_file(request, mime_type)) ++ copy_stream(fp, get_writeready_server_file(request, mime_type, content_length)) + fp.close() + + def rcsdiff_date_reformat(date_str, cfg):