diff --git a/.gitignore b/.gitignore index 20a73ea..292de41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,36 +1,37 @@ # NOTE: Thinking about adding files created by your operating system, IDE, # or text editor here? Don't! Add them to your per-user .gitignore instead. # Diviner /docs/ /.divinercache/ # XHPAST /support/xhpast/*.a /support/xhpast/*.o /support/xhpast/parser.yacc.output /support/xhpast/node_names.hpp /support/xhpast/xhpast /support/xhpast/xhpast.exe /src/parser/xhpast/bin/xhpast ## NOTE: Don't .gitignore these files! Even though they're build artifacts, we ## want to check them in so users can build xhpast without flex/bison. # /support/xhpast/parser.yacc.cpp # /support/xhpast/parser.yacc.hpp # /support/xhpast/scanner.lex.cpp # /support/xhpast/scanner.lex.hpp # This is an OS X build artifact. /support/xhpast/xhpast.dSYM # libphutil /src/.phutil_module_cache +/src/.cache /support/phutiltestlib/.phutil_module_cache # This file overrides "default.pem" if present. /resources/ssl/custom.pem # User extensions /externals/includes/* /src/extensions/* diff --git a/src/aphront/writeguard/AphrontWriteGuard.php b/src/aphront/writeguard/AphrontWriteGuard.php index 41f2485..589a0db 100644 --- a/src/aphront/writeguard/AphrontWriteGuard.php +++ b/src/aphront/writeguard/AphrontWriteGuard.php @@ -1,267 +1,267 @@ dispose(); * * Normally, you do not need to manage guards yourself -- the Aphront stack * handles it for you. * * This class accepts a callback, which will be invoked when a write is * attempted. The callback should validate the presence of a CSRF token in * the request, or abort the request (e.g., by throwing an exception) if a * valid token isn't present. * * @param callable CSRF callback. * @return this * @task manage */ public function __construct($callback) { if (self::$instance) { throw new Exception( pht( 'An %s already exists. Dispose of the previous guard '. 'before creating a new one.', __CLASS__)); } if (self::$allowUnguardedWrites) { throw new Exception( pht( 'An %s is being created in a context which permits '. 'unguarded writes unconditionally. This is not allowed and '. 'indicates a serious error.', __CLASS__)); } $this->callback = $callback; self::$instance = $this; } /** * Dispose of the active write guard. You must call this method when you are * done with a write guard. You do not normally need to call this yourself. * * @return void * @task manage */ public function dispose() { if (!self::$instance) { throw new Exception(pht( 'Attempting to dispose of write guard, but no write guard is active!')); } if ($this->allowDepth > 0) { throw new Exception( pht( - 'Imbalanced %s: more % calls than %s calls.', + 'Imbalanced %s: more %s calls than %s calls.', __CLASS__, 'beginUnguardedWrites()', 'endUnguardedWrites()')); } self::$instance = null; } /** * Determine if there is an active write guard. * * @return bool * @task manage */ public static function isGuardActive() { return (bool)self::$instance; } /** * Return on instance of AphrontWriteGuard if it's active, or null * * @return AphrontWriteGuard|null */ public static function getInstance() { return self::$instance; } /* -( Protecting Writes )-------------------------------------------------- */ /** * Declare intention to perform a write, validating that writes are allowed. * You should call this method before executing a write whenever you implement * a new storage engine where information can be permanently kept. * * Writes are permitted if: * * - The request has valid CSRF tokens. * - Unguarded writes have been temporarily enabled by a call to * @{method:beginUnguardedWrites}. * - All write guarding has been disabled with * @{method:allowDangerousUnguardedWrites}. * * If none of these conditions are true, this method will throw and prevent * the write. * * @return void * @task protect */ public static function willWrite() { if (!self::$instance) { if (!self::$allowUnguardedWrites) { throw new Exception( pht( 'Unguarded write! There must be an active %s to perform writes.', __CLASS__)); } else { // Unguarded writes are being allowed unconditionally. return; } } $instance = self::$instance; if ($instance->allowDepth == 0) { call_user_func($instance->callback); } } /* -( Disabling Write Protection )----------------------------------------- */ /** * Enter a scope which permits unguarded writes. This works like * @{method:beginUnguardedWrites} but returns an object which will end * the unguarded write scope when its __destruct() method is called. This * is useful to more easily handle exceptions correctly in unguarded write * blocks: * * // Restores the guard even if do_logging() throws. * function unguarded_scope() { * $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); * do_logging(); * } * * @return AphrontScopedUnguardedWriteCapability Object which ends unguarded * writes when it leaves scope. * @task disable */ public static function beginScopedUnguardedWrites() { self::beginUnguardedWrites(); return new AphrontScopedUnguardedWriteCapability(); } /** * Begin a block which permits unguarded writes. You should use this very * sparingly, and only for things like logging where CSRF is not a concern. * * You must pair every call to @{method:beginUnguardedWrites} with a call to * @{method:endUnguardedWrites}: * * AphrontWriteGuard::beginUnguardedWrites(); * do_logging(); * AphrontWriteGuard::endUnguardedWrites(); * * @return void * @task disable */ public static function beginUnguardedWrites() { if (!self::$instance) { return; } self::$instance->allowDepth++; } /** * Declare that you have finished performing unguarded writes. You must * call this exactly once for each call to @{method:beginUnguardedWrites}. * * @return void * @task disable */ public static function endUnguardedWrites() { if (!self::$instance) { return; } if (self::$instance->allowDepth <= 0) { throw new Exception( pht( 'Imbalanced %s: more %s calls than %s calls.', __CLASS__, 'endUnguardedWrites()', 'beginUnguardedWrites()')); } self::$instance->allowDepth--; } /** * Allow execution of unguarded writes. This is ONLY appropriate for use in * script contexts or other contexts where you are guaranteed to never be * vulnerable to CSRF concerns. Calling this method is EXTREMELY DANGEROUS * if you do not understand the consequences. * * If you need to perform unguarded writes on an otherwise guarded workflow * which is vulnerable to CSRF, use @{method:beginUnguardedWrites}. * * @return void * @task disable */ public static function allowDangerousUnguardedWrites($allow) { if (self::$instance) { throw new Exception( pht( 'You can not unconditionally disable %s by calling %s while a write '. 'guard is active. Use %s to temporarily allow unguarded writes.', __CLASS__, __FUNCTION__.'()', 'beginUnguardedWrites()')); } self::$allowUnguardedWrites = true; } }