Skip to main content
Glama

OpenFGA MCP

DSLParserTarget.php7.44 kB
<?php declare(strict_types=1); namespace OpenFGA\MCP\Tests\Fuzzing\Targets; use Exception; use Throwable; use function in_array; use function strlen; /** * Fuzzing target for OpenFGA DSL parsing. */ final class DSLParserTarget { private const MAX_DSL_LENGTH = 50000; public function fuzz(string $input): void { // Limit input size to prevent memory exhaustion if (self::MAX_DSL_LENGTH < strlen($input)) { $input = substr($input, 0, self::MAX_DSL_LENGTH); } // Simulate DSL parsing validation $this->validateDSLSyntax($input); // Check for recursive definitions that could cause infinite loops $this->checkForRecursion($input); // Validate type names and relations $this->validateIdentifiers($input); } public function getInitialCorpus(): array { return [ // Valid minimal DSL 'model schema 1.1 type user', // Valid DSL with relations 'model schema 1.1 type user type document relations define viewer: [user] define editor: [user] define owner: [user] or editor', // DSL with complex conditions 'model schema 1.1 type user type group relations define member: [user] type document relations define viewer: [user, group#member] define editor: [user] and viewer define owner: [user] or editor define can_share: owner or editor define can_delete: owner', // Edge cases 'model schema 1.1', 'model\nschema 1.1\ntype ', 'model\n schema 1.1\ntype user\n relations\n define owner: [user:*]', 'model\n schema 1.1\ntype ' . str_repeat('a', 100), // Potentially problematic inputs 'model\n schema 1.1\ntype user\n relations\n define a: b\n define b: a', 'model\n schema 1.1\ntype user#member', 'model\n schema 1.1\ntype user@test', 'model\n schema 1.1\ntype user\n relations\n define test: [user] or test', // Special characters 'model\n schema 1.1\ntype user\x00', 'model\n schema 1.1\ntype user\n\t\trelations\n\t\t\tdefine owner: [user]', 'модель\n схема 1.1\nтип пользователь', // Cyrillic 'model\n schema 1.1\ntype 用户', // Chinese 'model\n schema 1.1\ntype user👤', ]; } public function isExpectedError(Throwable $e): bool { // These are expected validation errors from invalid DSL syntax $expectedMessages = [ 'Missing model declaration', 'Missing or invalid schema version', 'Unbalanced brackets', 'Unbalanced braces', 'Reserved word used as type name', 'Invalid type name', 'Invalid relation name', 'Line too long', 'Type name too long', 'Relation name too long', 'DSL too complex', 'Missing type declaration', 'Invalid character in identifier', ]; foreach ($expectedMessages as $expected) { if (false !== stripos($e->getMessage(), $expected)) { return true; } } return false; } private function checkCircularDependency( string $current, array $relations, array &$visited, array $path, ): void { if (in_array($current, $path, true)) { // Circular dependency detected // In fuzzing, we note this but don't necessarily throw return; } if (isset($visited[$current])) { return; } $visited[$current] = true; $path[] = $current; // This is simplified - real implementation would parse the definition properly if (isset($relations[$current])) { foreach ($relations as $name => $definition) { if (false !== stripos($relations[$current], $name)) { $this->checkCircularDependency($name, $relations, $visited, $path); } } } } private function checkForRecursion(string $dsl): void { // Extract relation definitions preg_match_all('/define\s+(\w+):\s*(.+)$/m', $dsl, $matches); if (empty($matches[1])) { return; } $relations = array_combine($matches[1], $matches[2]); // Simple recursion check - in reality this would need to be more sophisticated foreach ($relations as $name => $definition) { if (false !== stripos($definition, $name)) { // Potential self-reference - in OpenFGA this might be valid // but we should ensure it doesn't create infinite loops } } // Check for circular dependencies $visited = []; foreach (array_keys($relations) as $relation) { $this->checkCircularDependency($relation, $relations, $visited, []); } } private function validateDSLSyntax(string $dsl): void { // Check for required model header if (false === stripos($dsl, 'model')) { throw new Exception('Missing model declaration'); } // Check for schema version if (! preg_match('/schema\s+\d+\.\d+/i', $dsl)) { throw new Exception('Missing or invalid schema version'); } // Count braces/brackets to check for balance $openBraces = substr_count($dsl, '{'); $closeBraces = substr_count($dsl, '}'); if ($openBraces !== $closeBraces) { throw new Exception('Unbalanced braces'); } $openBrackets = substr_count($dsl, '['); $closeBrackets = substr_count($dsl, ']'); if ($openBrackets !== $closeBrackets) { throw new Exception('Unbalanced brackets'); } // Check for extremely long lines that might indicate an attack $lines = explode("\n", $dsl); foreach ($lines as $line) { if (1000 < strlen($line)) { throw new Exception('Line too long'); } } } private function validateIdentifiers(string $dsl): void { // Extract type names preg_match_all('/type\s+(\S+)/m', $dsl, $typeMatches); foreach ($typeMatches[1] ?? [] as $typeName) { if (256 < strlen($typeName)) { throw new Exception('Type name too long'); } // Check for invalid characters in type names if (! preg_match('/^[a-zA-Z][a-zA-Z0-9_]*$/', $typeName)) { // Note: OpenFGA might have different rules, adjust as needed } // Check for reserved words $reserved = ['model', 'schema', 'type', 'relations', 'define', 'and', 'or', 'not']; if (in_array(strtolower($typeName), $reserved, true)) { throw new Exception('Reserved word used as type name'); } } // Extract relation names preg_match_all('/define\s+(\w+):/m', $dsl, $relationMatches); foreach ($relationMatches[1] ?? [] as $relationName) { if (256 < strlen($relationName)) { throw new Exception('Relation name too long'); } } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/evansims/openfga-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server