{"record":{"id":"9a91edc55f34e8f2","repo":"twigphp/Twig","slug":"calling-s-method-on-a-s-object-is-not-allowed","errorCode":null,"errorMessage":"Calling \"%s\" method on a \"%s\" object is not allowed.","messagePattern":"Calling \"(.+?)\" method on a \"(.+?)\" object is not allowed\\.","errorType":"exception","errorClass":"SecurityNotAllowedMethodError","httpStatus":null,"severity":"error","filePath":"src/Sandbox/SecurityPolicy.php","lineNumber":164,"sourceCode":"\n    public function checkMethodAllowed($obj, $method): void\n    {\n        if ($obj instanceof Template || $obj instanceof Markup) {\n            return;\n        }\n\n        $allowed = false;\n        $method = strtolower($method);\n        foreach ($this->allowedMethods as $class => $methods) {\n            if ($obj instanceof $class && \\in_array($method, $methods, true)) {\n                $allowed = true;\n                break;\n            }\n        }\n\n        if (!$allowed) {\n            $class = $obj::class;\n            throw new SecurityNotAllowedMethodError(\\sprintf('Calling \"%s\" method on a \"%s\" object is not allowed.', $method, $class), $class, $method);\n        }\n    }\n\n    public function checkPropertyAllowed($obj, $property): void\n    {\n        $allowed = false;\n        foreach ($this->allowedProperties as $class => $properties) {\n            if ($obj instanceof $class && \\in_array($property, \\is_array($properties) ? $properties : [$properties], true)) {\n                $allowed = true;\n                break;\n            }\n        }\n\n        if (!$allowed) {\n            $class = $obj::class;\n            throw new SecurityNotAllowedPropertyError(\\sprintf('Calling \"%s\" property on a \"%s\" object is not allowed.', $property, $class), $class, $property);\n        }\n    }","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/Sandbox/SecurityPolicy.php#L146-L182","documentation":"checkMethodAllowed() throws SecurityNotAllowedMethodError when sandboxed code calls a method on an object whose class is not covered by any allowlisted class/method pair in $allowedMethods. The sandbox calls this check before any method invocation on objects passed into the template.","triggerScenarios":"Inside a sandbox, a template (or macro, as in testMacroNamespaceDoesNotBenefitFromTheTemplateSandboxExemption) invokes e.g. $obj->getName() where 'getName' is not listed for $obj's class in the policy's allowed methods map; passing a new object type into the sandbox without updating the policy.","commonSituations":"Macros/namespaces failing to inherit looser rules from the outer template; policies written for one DTO reused with new object types; adding getters to a class without extending the whitelist.","solutions":["Add the method for that class: $policy->setAllowedMethods([MyClass::class => ['getName', ...]]) or extend the constructor's $allowedMethods array.","Catch SecurityNotAllowedMethodError in the renderer to log class and method names and adjust the policy.","Limit what is passed into sandboxed templates to fully allowlisted value objects.","Audit macro templates too — they get the same (or stricter) sandbox checks."],"exampleFix":"// before\n $policy = new SecurityPolicy($tags, $filters, [], [], []); // Foo::bar blocked\n// after\n $policy->setAllowedMethods([Foo::class => ['bar', 'getBaz']]);","handlingStrategy":"try-catch","validationCode":"if (!in_array($method, $policy->getAllowedMethods()[$obj::class] ?? [], true)) {\n    // would throw; extend allowlist or use a view-model\n}","typeGuard":"function methodAllowed(object $obj, string $method, array $allowedMethods): bool {\n    foreach ($allowedMethods[$obj::class] ?? [] as $rule) {\n        if ($rule === $method || str_starts_with($method, $rule)) return true;\n    }\n    return false;\n}","tryCatchPattern":"try {\n    $html = $twig->render($tpl, ['model' => $obj]);\n} catch (\\Twig\\Sandbox\\SecurityNotAllowedMethodError $e) {\n    $logger->warning('Sandbox blocked method', ['class' => $e->getClassName(), 'method' => $e->getMethodName()]);\n}","preventionTips":["Map every class exposed to sandboxes with its allowed methods.","Use dedicated read-only view-models instead of domain objects.","Remember macros/namespaces get no sandbox exemption.","Log blocked calls in staging to discover needed allowlist entries."],"tags":["twig","sandbox","security-policy","methods"],"backgroundTag":"permission-denied","analyzedSha":"a414c3a491defb5a60f2fc88ef79ff37c90010cd","analyzedAt":"2026-09-13T15:10:46.849Z","contentChangedAt":"2026-09-13T15:10:46.849Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}