VYPR
Critical severityNVD Advisory· Published Mar 24, 2008· Updated Apr 23, 2026

CVE-2008-1475

CVE-2008-1475

Description

The xml-rpc server in Roundup 1.4.4 does not check property permissions, which allows attackers to bypass restrictions and edit or read restricted properties via the (1) list, (2) display, and (3) set methods.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
roundupPyPI
< 1.4.51.4.5

Affected products

94
  • cpe:2.3:a:roundup-tracker:roundup:*:*:*:*:*:*:*:*+ 93 more
    • cpe:2.3:a:roundup-tracker:roundup:*:*:*:*:*:*:*:*range: <=1.4.3
    • cpe:2.3:a:roundup-tracker:roundup:0.1.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.1.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.1.2:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.1.3:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.2.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.2.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.2.2:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.2.3:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.2.4:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.2.5:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.2.6:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.2.7:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.2.8:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.3.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.3.0:pre1:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.3.0:pre2:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.3.0:pre3:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.4.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.4.0:b1:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.4.0:b2:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.4.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.4.2:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.4.2:pr1:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.0:beta1:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.0:beta2:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.0:pr1:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.2:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.3:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.4:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.5:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.6:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.7:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.8:stable:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.5.9:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.0:b1:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.0:b2:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.0:b3:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.0:b4:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.10:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.11:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.2:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.3:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.4:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.5:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.6:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.7:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.8:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.6.9:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.0:b1:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.0:b2:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.0:b3:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.10:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.11:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.12:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.2:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.3:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.4:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.5:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.6:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.7:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.8:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.7.9:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.8.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.8.0:b1:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.8.0:b2:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.8.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.8.2:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.8.3:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.8.4:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.8.5:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.8.6:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:0.9.0:b1:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.0.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.1.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.1.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.1.2:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.2.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.2.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.3.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.3.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.3.2:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.3.3:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.4.0:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.4.1:*:*:*:*:*:*:*
    • cpe:2.3:a:roundup-tracker:roundup:1.4.2:*:*:*:*:*:*:*

Patches

1
c00b7e5801f8

xml-rpc security checks and tests across all backends [SF#1907211]

https://github.com/roundup-tracker/roundupRichard JonesMar 7, 2008via ghsa
5 files changed · +82 58
  • CHANGES.txt+1 0 modified
    @@ -4,6 +4,7 @@ are given with the most recent entry first.
     2008-03-01 1.4.5
     Fixed:
     - 'Make a Copy' failed with more than one person in nosy list (sf #1906147)
    +- xml-rpc security checks and tests across all backends (sf #1907211)
     
     
     2008-03-01 1.4.4
    
  • roundup/xmlrpc.py+49 52 modified
    @@ -63,13 +63,10 @@ def __init__(self, tracker, username, password):
         def close(self):
             """Close the database, after committing any changes, if needed."""
     
    -        if getattr(self, 'db'):
    -            try:
    -                if self.db.transactions:
    -                    self.db.commit()
    -            finally:
    -                self.db.close()
    -
    +        try:
    +            self.db.commit()
    +        finally:
    +            self.db.close()
     
         def get_class(self, classname):
             """Return the class for the given classname."""
    @@ -94,7 +91,7 @@ def props_from_args(self, cl, args):
                 if value:
                     try:
                         props[key] = hyperdb.rawToHyperdb(self.db, cl, None,
    -                                                      key, value)
    +                        key, value)
                     except hyperdb.HyperdbValueError, message:
                         raise UsageError, message
                 else:
    @@ -115,51 +112,53 @@ def __init__(self, tracker, verbose = False):
     
         def list(self, username, password, classname, propname=None):
             r = RoundupRequest(self.tracker, username, password)
    -        cl = r.get_class(classname)
    -        if not propname:
    -            propname = cl.labelprop()
    -        def has_perm(itemid):
    -            return True
    -            r.db.security.hasPermission('View', r.userid, classname,
    -                itemid=itemid, property=propname)
    -        result = [cl.get(id, propname) for id in cl.list()
    -            if has_perm(id)]
    -        r.close()
    +        try:
    +            cl = r.get_class(classname)
    +            if not propname:
    +                propname = cl.labelprop()
    +            result = [cl.get(itemid, propname)
    +                for itemid in cl.list()
    +                     if r.db.security.hasPermission('View', r.userid,
    +                         classname, propname, itemid)
    +            ]
    +        finally:
    +            r.close()
             return result
     
         def display(self, username, password, designator, *properties):
             r = RoundupRequest(self.tracker, username, password)
    -        classname, itemid = hyperdb.splitDesignator(designator)
    -
    -        if not r.db.security.hasPermission('View', r.userid, classname,
    -                itemid=itemid):
    -            raise Unauthorised('Permission to view %s denied'%designator)
    -
    -        cl = r.get_class(classname)
    -        props = properties and list(properties) or cl.properties.keys()
    -        props.sort()
    -        result = [(property, cl.get(itemid, property)) for property in props]
    -        r.close()
    +        try:
    +            classname, itemid = hyperdb.splitDesignator(designator)
    +            cl = r.get_class(classname)
    +            props = properties and list(properties) or cl.properties.keys()
    +            props.sort()
    +            for p in props:
    +                if not r.db.security.hasPermission('View', r.userid,
    +                        classname, p, itemid):
    +                    raise Unauthorised('Permission to view %s of %s denied'%
    +                            (p, designator))
    +            result = [(prop, cl.get(itemid, prop)) for prop in props]
    +        finally:
    +            r.close()
             return dict(result)
     
         def create(self, username, password, classname, *args):
             r = RoundupRequest(self.tracker, username, password)
    +        try:
    +            if not r.db.security.hasPermission('Create', r.userid, classname):
    +                raise Unauthorised('Permission to create %s denied'%classname)
     
    -        if not r.db.security.hasPermission('Create', r.userid, classname):
    -            raise Unauthorised('Permission to create %s denied'%classname)
    -
    -        cl = r.get_class(classname)
    +            cl = r.get_class(classname)
     
    -        # convert types
    -        props = r.props_from_args(cl, args)
    +            # convert types
    +            props = r.props_from_args(cl, args)
     
    -        # check for the key property
    -        key = cl.getkey()
    -        if key and not props.has_key(key):
    -            raise UsageError, 'you must provide the "%s" property.'%key
    +            # check for the key property
    +            key = cl.getkey()
    +            if key and not props.has_key(key):
    +                raise UsageError, 'you must provide the "%s" property.'%key
     
    -        # do the actual create
    -        try:
    +            # do the actual create
                 try:
                     result = cl.create(**props)
                 except (TypeError, IndexError, ValueError), message:
    @@ -170,19 +169,17 @@ def create(self, username, password, classname, *args):
     
         def set(self, username, password, designator, *args):
             r = RoundupRequest(self.tracker, username, password)
    -        classname, itemid = hyperdb.splitDesignator(designator)
    -
    -        if not r.db.security.hasPermission('Edit', r.userid, classname,
    -                itemid=itemid):
    -            raise Unauthorised('Permission to edit %s denied'%designator)
    -
    -        cl = r.get_class(classname)
    -
    -        # convert types
    -        props = r.props_from_args(cl, args)
             try:
    +            classname, itemid = hyperdb.splitDesignator(designator)
    +            cl = r.get_class(classname)
    +            props = r.props_from_args(cl, args) # convert types
    +            for p in props.iterkeys ():
    +                if not r.db.security.hasPermission('Edit', r.userid,
    +                        classname, p, itemid):
    +                    raise Unauthorised('Permission to edit %s of %s denied'%
    +                        (p, designator))
                 try:
    -                cl.set(itemid, **props)
    +                return cl.set(itemid, **props)
                 except (TypeError, IndexError, ValueError), message:
                     raise UsageError, message
             finally:
    
  • test/db_test_base.py+15 2 modified
    @@ -15,7 +15,7 @@
     # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
     # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     #
    -# $Id: db_test_base.py,v 1.96 2008-02-07 03:28:34 richard Exp $
    +# $Id: db_test_base.py,v 1.97 2008-03-07 01:11:55 richard Exp $
     
     import unittest, os, shutil, errno, imp, sys, time, pprint, sets, base64, os.path
     
    @@ -62,6 +62,7 @@ def setupTracker(dirname, backend="anydbm"):
         tracker = instance.open(dirname)
         if tracker.exists():
             tracker.nuke()
    +        init.write_select_db(dirname, backend)
         tracker.init(password.Password('sekrit'))
         return tracker
     
    @@ -293,7 +294,7 @@ def testMultilinkChangeIterable(self):
                 l = [u1,u2]; l.sort()
                 m = self.db.issue.get(nid, "nosy"); m.sort()
                 self.assertEqual(l, m)
    -       
    +
     
     # XXX one day, maybe...
     #    def testMultilinkOrdering(self):
    @@ -329,6 +330,18 @@ def testDateChange(self):
                     c = self.db.issue.get(nid, "deadline")
                     self.assertEqual(c, d)
     
    +    def testDateLeapYear(self):
    +        nid = self.db.issue.create(title='spam', status='1',
    +            deadline=date.Date('2008-02-29'))
    +        self.assertEquals(str(self.db.issue.get(nid, 'deadline')),
    +            '2008-02-29.00:00:00')
    +        self.db.issue.set(nid, deadline=date.Date('2008-02-29'))
    +        self.assertEquals(str(self.db.issue.get(nid, 'deadline')),
    +            '2008-02-29.00:00:00')
    +        self.assertEquals(self.db.issue.filter(None, {'deadline': '2008-02-29'}),
    +            [nid])
    +
    +
         def testDateUnset(self):
             for commit in (0,1):
                 nid = self.db.issue.create(title="spam", status='1')
    
  • test/test_dates.py+4 1 modified
    @@ -15,7 +15,7 @@
     # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
     # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     #
    -# $Id: test_dates.py,v 1.44 2007-12-23 00:23:23 richard Exp $
    +# $Id: test_dates.py,v 1.45 2008-03-07 01:11:55 richard Exp $
     from __future__ import nested_scopes
     
     import unittest
    @@ -68,6 +68,9 @@ def testDate(self):
             ae(str(Date('1900-02-01')), '1900-02-01.00:00:00')
             ae(str(Date('1800-07-15')), '1800-07-15.00:00:00')
     
    +    def testLeapYear(self):
    +        self.assertEquals(str(Date('2008-02-29')), '2008-02-29.00:00:00')
    +
         def testDateError(self):
             self.assertRaises(ValueError, Date, "12")
             # Date cannot handle dates before year 1
    
  • test/test_xmlrpc.py+13 3 modified
    @@ -9,16 +9,20 @@
     from roundup.cgi.exceptions import *
     from roundup import init, instance, password, hyperdb, date
     from roundup.xmlrpc import RoundupServer
    +from roundup.backends import list_backends
     
     import db_test_base
     
     NEEDS_INSTANCE = 1
     
     class TestCase(unittest.TestCase):
    +
    +    backend = None
    +
         def setUp(self):
             self.dirname = '_test_xmlrpc'
             # set up and open a tracker
    -        self.instance = db_test_base.setupTracker(self.dirname)
    +        self.instance = db_test_base.setupTracker(self.dirname, self.backend)
     
             # open the database
             self.db = self.instance.open('admin')
    @@ -55,6 +59,10 @@ def testChange(self):
                 'realname')
             self.assertEqual(results['realname'], 'Joe Doe')
     
    +        # check we can't change admin's details
    +        self.assertRaises(Unauthorised, self.server.set, 'joe', 'random',
    +            'user1', 'realname=Joe Doe')
    +
         def testCreate(self):
             results = self.server.create('joe', 'random', 'issue', 'title=foo')
             issueid = 'issue' + results
    @@ -89,10 +97,12 @@ def testAuthAllowedCreate(self):
     
     def test_suite():
         suite = unittest.TestSuite()
    -    suite.addTest(unittest.makeSuite(TestCase))
    +    for l in list_backends():
    +        dct = dict(backend = l)
    +        subcls = type(TestCase)('TestCase_%s'%l, (TestCase,), dct)
    +        suite.addTest(unittest.makeSuite(subcls))
         return suite
     
     if __name__ == '__main__':
         runner = unittest.TextTestRunner()
         unittest.main(testRunner=runner)
    -
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

22

News mentions

0

No linked articles in our index yet.