• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / Zope / 3956162881

pending completion
3956162881

push

github

Michael Howitz
Update to deprecation warning free releases.

4401 of 7036 branches covered (62.55%)

Branch coverage included in aggregate %.

27161 of 31488 relevant lines covered (86.26%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.43
/src/Products/PageTemplates/expression.py
1
"""``chameleon.tales`` expressions."""
2

3
import warnings
1✔
4
from ast import NodeTransformer
1✔
5
from ast import parse
1✔
6

7
from chameleon.astutil import Static
1✔
8
from chameleon.astutil import Symbol
1✔
9
from chameleon.codegen import template
1✔
10
from chameleon.tales import NotExpr
1✔
11
from chameleon.tales import StringExpr
1✔
12

13
from AccessControl.SecurityManagement import getSecurityManager
1✔
14
from AccessControl.ZopeGuards import guarded_apply
1✔
15
from AccessControl.ZopeGuards import guarded_getattr
1✔
16
from AccessControl.ZopeGuards import guarded_getitem
1✔
17
from AccessControl.ZopeGuards import guarded_iter
1✔
18
from AccessControl.ZopeGuards import protected_inplacevar
1✔
19
from OFS.interfaces import ITraversable
1✔
20
from RestrictedPython import RestrictingNodeTransformer
1✔
21
from RestrictedPython.Utilities import utility_builtins
1✔
22
from z3c.pt import expressions
1✔
23
from zExceptions import NotFound
1✔
24
from zExceptions import Unauthorized
1✔
25
from zope.interface import implementer
1✔
26
from zope.tales.tales import ExpressionEngine
1✔
27
from zope.traversing.adapters import traversePathElement
1✔
28
from zope.traversing.interfaces import TraversalError
1✔
29

30
from .Expressions import render
1✔
31
from .interfaces import IZopeAwareEngine
1✔
32

33

34
_marker = object()
1✔
35

36
zope2_exceptions = (
1✔
37
    AttributeError,
38
    LookupError,
39
    NameError,
40
    TypeError,
41
    ValueError,
42
    NotFound,
43
    Unauthorized,
44
    TraversalError,
45
)
46

47

48
def static(obj):
1✔
49
    return Static(template("obj", obj=Symbol(obj), mode="eval"))
1✔
50

51

52
class BoboAwareZopeTraverse:
1✔
53
    traverse_method = 'restrictedTraverse'
1✔
54

55
    __slots__ = ()
1✔
56

57
    @classmethod
1✔
58
    def traverse(cls, base, request, path_items):
1✔
59
        """See ``zope.app.pagetemplate.engine``."""
60

61
        validate = getSecurityManager().validate
1✔
62
        path_items = list(path_items)
1✔
63
        path_items.reverse()
1✔
64

65
        while path_items:
1✔
66
            name = path_items.pop()
1✔
67

68
            if ITraversable.providedBy(base):
1✔
69
                base = getattr(base, cls.traverse_method)(name)
1✔
70
            else:
71
                found = traversePathElement(base, name, path_items,
1✔
72
                                            request=request)
73

74
                # If traverse_method is something other than
75
                # ``restrictedTraverse`` then traversal is assumed to be
76
                # unrestricted. This emulates ``unrestrictedTraverse``
77
                if cls.traverse_method != 'restrictedTraverse':
1!
78
                    base = found
×
79
                    continue
×
80

81
                # Special backwards compatibility exception for the name ``_``,
82
                # which was often used for translation message factories.
83
                # Allow and continue traversal.
84
                if name == '_':
1!
85
                    warnings.warn('Traversing to the name `_` is deprecated '
×
86
                                  'and will be removed in Zope 6.',
87
                                  DeprecationWarning)
88
                    base = found
×
89
                    continue
×
90

91
                # All other names starting with ``_`` are disallowed.
92
                # This emulates what restrictedTraverse does.
93
                if name.startswith('_'):
1!
94
                    raise NotFound(name)
×
95

96
                # traversePathElement doesn't apply any Zope security policy,
97
                # so we validate access explicitly here.
98
                try:
1✔
99
                    validate(base, base, name, found)
1✔
100
                    base = found
1✔
101
                except Unauthorized:
×
102
                    # Convert Unauthorized to prevent information disclosures
103
                    raise NotFound(name)
×
104

105
        return base
1✔
106

107
    def __call__(self, base, econtext, call, path_items):
1✔
108
        request = econtext.get('request')
1✔
109

110
        if path_items:
1✔
111
            base = self.traverse(base, request, path_items)
1✔
112

113
        if call is False:
1!
114
            return base
×
115

116
        if getattr(base, '__call__', _marker) is not _marker or \
1✔
117
           callable(base):
118
            base = render(base, econtext)
1✔
119

120
        return base
1✔
121

122

123
class TrustedBoboAwareZopeTraverse(BoboAwareZopeTraverse):
1✔
124
    traverse_method = 'unrestrictedTraverse'
1✔
125

126
    __slots__ = ()
1✔
127

128
    def __call__(self, base, econtext, call, path_items):
1✔
129
        request = econtext.get('request')
×
130

131
        base = self.traverse(base, request, path_items)
×
132

133
        if call is False:
×
134
            return base
×
135

136
        if getattr(base, '__call__', _marker) is not _marker or \
×
137
           isinstance(base, type):
138
            return base()
×
139

140
        return base
×
141

142

143
class PathExpr(expressions.PathExpr):
1✔
144
    exceptions = zope2_exceptions
1✔
145

146
    traverser = Static(template(
1✔
147
        "cls()", cls=Symbol(BoboAwareZopeTraverse), mode="eval"
148
    ))
149

150

151
class TrustedPathExpr(PathExpr):
1✔
152
    traverser = Static(template(
1✔
153
        "cls()", cls=Symbol(TrustedBoboAwareZopeTraverse), mode="eval"
154
    ))
155

156

157
class NocallExpr(expressions.NocallExpr, PathExpr):
1✔
158
    pass
1✔
159

160

161
class ExistsExpr(expressions.ExistsExpr):
1✔
162
    exceptions = zope2_exceptions
1✔
163

164

165
class RestrictionTransform(NodeTransformer):
1✔
166
    secured = {
1✔
167
        '_getattr_': guarded_getattr,
168
        '_getitem_': guarded_getitem,
169
        '_apply_': guarded_apply,
170
        '_getiter_': guarded_iter,
171
        '_inplacevar_': protected_inplacevar,
172
    }
173

174
    def visit_Name(self, node):
1✔
175
        value = self.secured.get(node.id)
1✔
176
        if value is not None:
1✔
177
            return Symbol(value)
1✔
178

179
        return node
1✔
180

181

182
class UntrustedPythonExpr(expressions.PythonExpr):
1✔
183
    restricted_python_transformer = RestrictingNodeTransformer()
1✔
184
    page_templates_expression_transformer = RestrictionTransform()
1✔
185

186
    # Make copy of parent expression builtins
187
    builtins = expressions.PythonExpr.builtins.copy()
1✔
188

189
    # Update builtins with Restricted Python utility builtins
190
    builtins.update({
1✔
191
        name: static(builtin) for (name, builtin) in utility_builtins.items()
192
    })
193

194
    def parse(self, string):
1✔
195
        encoded = string.encode('utf-8')
1✔
196
        node = parse(encoded, mode='eval')
1✔
197

198
        # Run Node Transformation from RestrictedPython:
199
        self.restricted_python_transformer.visit(node)
1✔
200

201
        # Run PageTemplate.expression RestrictedPython Transform:
202
        self.page_templates_expression_transformer.visit(node)
1✔
203

204
        return node
1✔
205

206

207
# Whether an engine is Zope aware does not depend on the class
208
# but how it is configured - especially, that is uses a Zope aware
209
# `PathExpr` implementation.
210
# Nevertheless, we mark the class as "Zope aware" for simplicity
211
# assuming that users of the class use a proper `PathExpr`
212
@implementer(IZopeAwareEngine)
1✔
213
class ChameleonEngine(ExpressionEngine):
1✔
214
    """Expression engine for ``chameleon.tales``.
215

216
    Only partially implemented: its ``compile`` is currently unusable
217
    """
218
    def compile(self, expression):
1✔
219
        raise NotImplementedError()
220

221

222
types = dict(
1✔
223
    python=UntrustedPythonExpr,
224
    string=StringExpr,
225
    not_=NotExpr,
226
    exists=ExistsExpr,
227
    path=PathExpr,
228
    provider=expressions.ProviderExpr,
229
    nocall=NocallExpr)
230

231

232
def createChameleonEngine(types=types, untrusted=True, **overrides):
1✔
233
    e = ChameleonEngine()
1✔
234

235
    def norm(k):
1✔
236
        return k[:-1] if k.endswith("_") else k
1✔
237

238
    e.untrusted = untrusted
1✔
239
    ts = e.types
1✔
240
    for k, v in types.items():
1✔
241
        k = norm(k)
1✔
242
        e.registerType(k, v)
1✔
243
    for k, v in overrides.items():
1✔
244
        k = norm(k)
1✔
245
        if k in ts:
1!
246
            del ts[k]
1✔
247
        e.registerType(k, v)
1✔
248
    return e
1✔
249

250

251
def createTrustedChameleonEngine(**overrides):
1✔
252
    ovr = dict(python=expressions.PythonExpr, path=TrustedPathExpr)
1✔
253
    ovr.update(overrides)
1✔
254
    return createChameleonEngine(untrusted=False, **ovr)
1✔
255

256

257
_engine = createChameleonEngine()
1✔
258

259

260
def getEngine():
1✔
261
    return _engine
1✔
262

263

264
_trusted_engine = createTrustedChameleonEngine()
1✔
265

266

267
def getTrustedEngine():
1✔
268
    return _trusted_engine
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc