}> */ public static function defaults(): array { $high_risk_probes = array(); foreach ( self::HIGH_RISK_ENTITIES as $entity ) { foreach ( array( 'create', 'update', 'delete' ) as $category ) { $high_risk_probes[] = array( 'action_cat' => $category, 'entity_type' => $entity, ); } } return array( array( 'condition' => 'Any read', 'decision' => 'ALLOW', 'detail' => 'Reading content or settings is never held. Reads are still recorded in the ledger.', 'probes' => array( array( 'action_cat' => 'read', 'entity_type' => 'post', ), array( 'action_cat' => 'read', 'entity_type' => 'option', ), array( 'action_cat' => 'read', 'entity_type' => 'user', ), ), ), array( 'condition' => 'Any write to ' . implode( ', ', self::HIGH_RISK_ENTITIES ), 'decision' => 'REQUIRE_APPROVAL', 'detail' => 'Settings, accounts, roles, code installation, and site-wide appearance (templates, global styles, widgets) are site-integrity changes, so they are held for a human even when creating or updating.', 'probes' => $high_risk_probes, ), array( 'condition' => 'Creating or updating other content', 'decision' => 'ALLOW', 'detail' => 'Posts, pages, media, comments, terms, menus and navigation are routine agent work.', 'probes' => array( array( 'action_cat' => 'create', 'entity_type' => 'post', ), array( 'action_cat' => 'update', 'entity_type' => 'page', ), array( 'action_cat' => 'create', 'entity_type' => 'media', ), array( 'action_cat' => 'update', 'entity_type' => 'comment', ), ), ), array( 'condition' => 'Any delete', 'decision' => 'REQUIRE_APPROVAL', 'detail' => 'Deletions are held for a human. Approved deletions are snapshotted so they can be undone.', 'probes' => array( array( 'action_cat' => 'delete', 'entity_type' => 'post', ), array( 'action_cat' => 'delete', 'entity_type' => 'page', ), ), ), array( 'condition' => 'Anything the firewall could not categorize', 'decision' => 'REQUIRE_APPROVAL', 'detail' => 'An action that does not resolve to a known category fails safe rather than open.', 'probes' => array( array( 'action_cat' => 'unknown', 'entity_type' => 'unknown', ), ), ), ); } /** * Decision values. * * @return list */ public static function decisions(): array { return array_map( static function ( Decision $decision ): string { return $decision->value; }, Decision::cases() ); } /** * JSON Schema for the rules array, in the draft-04 dialect WordPress * uses for REST schemas. * * @return array */ public static function json_schema(): array { return array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'adaf_policy_rules', 'description' => 'Ordered list of policy rules. The first rule whose match applies decides the action.', 'type' => 'array', 'items' => array( 'type' => 'object', 'properties' => array( 'match' => array( 'description' => 'Dimensions that must all apply for the rule to fire. Omit to match every action.', 'type' => 'object', 'properties' => array( 'ability' => array( 'description' => 'Ability name, optionally ending in * to match a prefix.', 'type' => 'string', 'pattern' => '^[A-Za-z0-9_-]+/[A-Za-z0-9_-]*\\*?$', ), 'action_cat' => array( 'description' => 'Action category.', 'type' => 'string', 'enum' => self::ACTION_CATS, ), 'entity_type' => array( 'description' => 'Target entity type. Core types are listed; abilities may mint others.', 'type' => 'string', 'pattern' => '^[a-z][a-z0-9_]*$', ), 'entity_id' => array( 'description' => 'A specific entity id.', 'type' => 'integer', 'minimum' => 1, ), 'source' => array( 'description' => 'Which interception point saw the action.', 'type' => 'string', 'enum' => self::SOURCES, ), ), 'additionalProperties' => false, ), 'limit' => array( 'description' => 'Sliding-window write cap. Exceeding it denies the action.', 'type' => 'object', 'properties' => array( 'writes' => array( 'type' => 'integer', 'minimum' => 1, ), 'window' => array( 'description' => 'Window length in seconds.', 'type' => 'integer', 'minimum' => 1, ), ), 'required' => array( 'writes', 'window' ), 'additionalProperties' => false, ), 'decision' => array( 'description' => 'Verdict when the rule fires. Omit only on a rule that carries a limit.', 'type' => 'string', 'enum' => self::decisions(), ), ), 'additionalProperties' => false, ), ); } }