Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEW: Obfuscate filenames to prevent IDE search issues #426

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions _config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ SilverStripe\Core\Injector\Injector:
constructor:
storeCreator: '%$SilverStripe\GraphQL\Schema\Interfaces\SchemaStorageCreator'

SilverStripe\GraphQL\Schema\Storage\NameObfuscator:
class: SilverStripe\GraphQL\Schema\Storage\HybridObfuscator

SilverStripe\GraphQL\Schema\Schema:
schemas: []
---
Expand All @@ -49,3 +52,22 @@ Only:
SilverStripe\TestSession\TestSessionEnvironment:
extensions:
- SilverStripe\GraphQL\Extensions\TestSessionEnvironmentExtension


---
Name: graphqlconfig-debug
Only:
envvarset: 'DEBUG_SCHEMA'
---
SilverStripe\Core\Injector\Injector:
SilverStripe\GraphQL\Schema\Storage\NameObfuscator:
class: SilverStripe\GraphQL\Schema\Storage\NaiveNameObfuscator

---
Name: graphqlconfig-live
Except:
environment: dev
---
SilverStripe\Core\Injector\Injector:
SilverStripe\GraphQL\Schema\Storage\NameObfuscator:
class: SilverStripe\GraphQL\Schema\Storage\HashNameObfuscator
8 changes: 8 additions & 0 deletions _config/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
Name: graphql-test
---
SilverStripe\Core\Injector\Injector:
SilverStripe\Dev\State\SapphireTestState:
properties:
States:
debugSchema: '%$SilverStripe\GraphQL\Dev\State\DebugSchemaState'
34 changes: 34 additions & 0 deletions src/Dev/State/DebugSchemaState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php


namespace SilverStripe\GraphQL\Dev\State;

use SilverStripe\Core\Environment;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Dev\State\TestState;
use SilverStripe\GraphQL\Schema\Storage\NaiveNameObfuscator;
use SilverStripe\GraphQL\Schema\Storage\NameObfuscator;

class DebugSchemaState implements TestState
{
public function setUp(SapphireTest $test)
{
// no-op
}

public function tearDown(SapphireTest $test)
{
// no-op
}

public function setUpOnce($class)
{
Environment::setEnv('DEBUG_SCHEMA', 1);
}

public function tearDownOnce($class)
{
// no-op
}
}
8 changes: 6 additions & 2 deletions src/Schema/Storage/AbstractTypeRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use GraphQL\Type\Definition\NonNull;
use GraphQL\Type\Definition\ListOfType;
use Exception;
use SilverStripe\Core\Injector\Injector;

abstract class AbstractTypeRegistry
{
Expand All @@ -34,12 +35,15 @@ abstract protected static function getSourceNamespace(): string;
*/
protected static function fromCache(string $typename)
{
/* @var NameObfuscator $obfuscator */
$obfuscator = Injector::inst()->get(NameObfuscator::class);
$type = null;
if (!isset(static::$types[$typename])) {
$file = static::getSourceDirectory() . DIRECTORY_SEPARATOR . $typename . '.php';
$obfuscatedName = $obfuscator->obfuscate($typename);
$file = static::getSourceDirectory() . DIRECTORY_SEPARATOR . $obfuscatedName . '.php';
if (file_exists($file)) {
require_once($file);
$cls = static::getSourceNamespace() . '\\' . $typename;
$cls = static::getSourceNamespace() . '\\' . $obfuscatedName;
if (class_exists($cls)) {
$type = new $cls();
}
Expand Down
46 changes: 44 additions & 2 deletions src/Schema/Storage/CodeGenerationStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Psr\SimpleCache\CacheInterface;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Path;
use SilverStripe\GraphQL\Schema\Exception\EmptySchemaException;
use SilverStripe\GraphQL\Schema\Exception\SchemaNotFoundException;
Expand Down Expand Up @@ -60,6 +61,13 @@ class CodeGenerationStore implements SchemaStorageInterface
*/
private static $dirName = '.graphql';

/**
* @var string[]
*/
private static $dependencies = [
'Obfuscator' => '%$' . NameObfuscator::class,
];

/**
* @var string
*/
Expand All @@ -85,6 +93,11 @@ class CodeGenerationStore implements SchemaStorageInterface
*/
private $graphqlSchema;

/**
* @var NameObfuscator
*/
private $obfuscator;

/**
* @param string $name
* @param CacheInterface $cache
Expand Down Expand Up @@ -140,9 +153,11 @@ public function persistSchema(StorableSchema $schema): void
}

$templateDir = static::getTemplateDir();
$obfuscator = $this->getObfuscator();
$globals = [
'typeClassName' => self::TYPE_CLASS_NAME,
'namespace' => $this->getNamespace(),
'obfuscator' => $obfuscator,
];

$config = $schema->getConfig()->toArray();
Expand Down Expand Up @@ -209,7 +224,8 @@ public function persistSchema(StorableSchema $schema): void
continue;
}
}
$file = Path::join($temp, $name . '.php');
$obfuscatedName = $obfuscator->obfuscate($name);
$file = Path::join($temp, $obfuscatedName . '.php');
$encoder = Encoder::create(Path::join($templateDir, $template), $type, $globals);
$code = $encoder->encode();
$fs->dumpFile($file, $this->toCode($code));
Expand All @@ -229,7 +245,14 @@ public function persistSchema(StorableSchema $schema): void

/* @var SplFileInfo $file */
foreach ($currentFiles as $file) {
$type = $file->getBasename('.php');
$contents = $file->getContents();
preg_match('/\/\/ @type:([A-Za-z0-9+_]+)/', $contents, $matches);
Schema::invariant(
$matches,
'Could not find type name in file %s',
$file->getPathname()
);
$type = $matches[1];
if (!in_array($type, $touched)) {
$fs->remove($file->getPathname());
$this->getCache()->delete($type);
Expand Down Expand Up @@ -384,6 +407,25 @@ public function setRootDir(string $rootDir): CodeGenerationStore
return $this;
}

/**
* @return NameObfuscator
*/
public function getObfuscator(): NameObfuscator
{
return $this->obfuscator;
}

/**
* @param NameObfuscator $obfuscator
* @return CodeGenerationStore
*/
public function setObfuscator(NameObfuscator $obfuscator): CodeGenerationStore
{
$this->obfuscator = $obfuscator;
return $this;
}


/**
* @return string
*/
Expand Down
19 changes: 19 additions & 0 deletions src/Schema/Storage/HashNameObfuscator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php


namespace SilverStripe\GraphQL\Schema\Storage;

/**
* For the most obscure approach, hash the file names so they're completely undiscoverable.
*/
class HashNameObfuscator implements NameObfuscator
{
/**
* @param string $name
* @return string
*/
public function obfuscate(string $name): string
{
return strtoupper($name[0]) . md5($name);
}
}
19 changes: 19 additions & 0 deletions src/Schema/Storage/HybridObfuscator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php


namespace SilverStripe\GraphQL\Schema\Storage;

/**
* Hashed for less discoverability, but still readable if you focus on it
*/
class HybridObfuscator implements NameObfuscator
{
/**
* @param string $name
* @return string
*/
public function obfuscate(string $name): string
{
return sprintf('%s_%s', $name, md5($name));
}
}
19 changes: 19 additions & 0 deletions src/Schema/Storage/NaiveNameObfuscator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php


namespace SilverStripe\GraphQL\Schema\Storage;

/**
* Naive implementation for debugging. Allow the class file name to be the same as the class
*/
class NaiveNameObfuscator implements NameObfuscator
{
/**
* @param string $name
* @return string
*/
public function obfuscate(string $name): string
{
return $name;
}
}
16 changes: 16 additions & 0 deletions src/Schema/Storage/NameObfuscator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php


namespace SilverStripe\GraphQL\Schema\Storage;

/**
* Defines a service that can obfuscate classnames to make their files less discoverable
*/
interface NameObfuscator
{
/**
* @param string $name
* @return string
*/
public function obfuscate(string $name): string;
}
4 changes: 3 additions & 1 deletion src/Schema/Storage/templates/enum.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

use GraphQL\Type\Definition\EnumType;

class <?=$enum->getName(); ?> extends EnumType
// @type:<?=$enum->getName(); ?>

class <?=$globals['obfuscator']->obfuscate($enum->getName()) ?> extends EnumType
{
public function __construct()
{
Expand Down
4 changes: 3 additions & 1 deletion src/Schema/Storage/templates/interface.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
use GraphQL\Type\Definition\InterfaceType;
use SilverStripe\GraphQL\Schema\Resolver\ComposedResolver;

class <?=$interface->getName(); ?> extends InterfaceType
// @type:<?=$interface->getName(); ?>

class <?=$globals['obfuscator']->obfuscate($interface->getName()) ?> extends InterfaceType
{
public function __construct()
{
Expand Down
4 changes: 3 additions & 1 deletion src/Schema/Storage/templates/scalar.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

use GraphQL\Type\Definition\CustomScalarType;

class <?=$scalar->getName(); ?> extends CustomScalarType
// @type:<?=$scalar->getName(); ?>

class <?=$globals['obfuscator']->obfuscate($scalar->getName()) ?> extends CustomScalarType
{
public function __construct()
{
Expand Down
4 changes: 3 additions & 1 deletion src/Schema/Storage/templates/type.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
use GraphQL\Type\Definition\InputObjectType;
use SilverStripe\GraphQL\Schema\Resolver\ComposedResolver;

// @type:<?=$type->getName(); ?>

class <?=$type->getName() ?> extends <?php if ($type->getIsInput()) :

class <?=$globals['obfuscator']->obfuscate($type->getName()) ?> extends <?php if ($type->getIsInput()) :
?>InputObjectType<?php
else :
?>ObjectType<?php
Expand Down
4 changes: 3 additions & 1 deletion src/Schema/Storage/templates/union.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
use GraphQL\Type\Definition\UnionType;
use SilverStripe\GraphQL\Schema\Resolver\ComposedResolver;

class <?=$union->getName() ?> extends UnionType
// @type:<?=$union->getName(); ?>

class <?=$globals['obfuscator']->obfuscate($union->getName()) ?> extends UnionType
{
public function __construct()
{
Expand Down
10 changes: 10 additions & 0 deletions src/Schema/Type/Enum.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ public function validate(): void
'Enum type %s has no values defined',
$this->getName()
);
$rx = '/^[_a-zA-Z][_a-zA-Z0-9]*$/';
foreach ($this->getValueList() as $item) {
Schema::invariant(
preg_match($rx, $item['Key']),
'Key "%s" for "%s" is not valid. Must match %s',
$item['Key'],
$this->getName(),
$rx
);
}
}

/**
Expand Down
Loading