1This directory contains the common infrastructure for the following tests (also referred below as projects). 2 3- referrer-policy/ 4- mixed-content/ 5- upgrade-insecure-requests/ 6 7Subdirectories: 8 9- `resources`: 10 Serves JavaScript test helpers. 11- `subresource`: 12 Serves subresources, with support for redirects, stash, etc. 13 The subresource paths are managed by `subresourceMap` and 14 fetched in `requestVia*()` functions in `resources/common.js`. 15- `scope`: 16 Serves nested contexts, such as iframe documents or workers. 17 Used from `invokeFrom*()` functions in `resources/common.js`. 18- `tools`: 19 Scripts that generate test HTML files. Not used while running tests. 20- `/referrer-policy/generic/subresource-test`: 21 Sanity checking tests for subresource invocation 22 (This is still placed outside common/) 23 24# Test generator 25 26The test generator ([common/security-features/tools/generate.py](tools/generate.py)) generates test HTML files from templates and a seed (`spec.src.json`) that defines all the test scenarios. 27 28The project (i.e. a WPT subdirectory, for example `referrer-policy/`) that uses the generator should define per-project data and invoke the common generator logic in `common/security-features/tools`. 29 30This is the overview of the project structure: 31 32``` 33common/security-features/ 34└── tools/ - the common test generator logic 35 ├── spec.src.json 36 └── template/ - the test files templates 37project-directory/ (e.g. referrer-policy/) 38├── spec.src.json 39├── generic/ 40│ ├── test-case.sub.js - Per-project test helper 41│ ├── sanity-checker.js (Used by debug target only) 42│ └── spec_json.js (Used by debug target only) 43└── gen/ - generated tests 44``` 45 46## Generating the tests 47 48Note: When the repository already contains generated tests, [remove all generated tests](#removing-all-generated-tests) first. 49 50```bash 51# Install json5 module if needed. 52pip install --user json5 53 54# Generate the test files under gen/ (HTMLs and .headers files). 55path/to/common/security-features/tools/generate.py --spec path/to/project-directory/ 56 57# Add all generated tests to the repo. 58git add path/to/project-directory/gen/ && git commit -m "Add generated tests" 59``` 60 61This will parse the spec JSON5 files and determine which tests to generate (or skip) while using templates. 62 63- The default spec JSON5: `common/security-features/tools/spec.src.json`. 64 - Describes common configurations, such as subresource types, source context types, etc. 65- The per-project spec JSON5: `project-directory/spec.src.json`. 66 - Describes project-specific configurations, particularly those related to test generation patterns (`specification`), policy deliveries (e.g. `delivery_type`, `delivery_value`) and `expectation`. 67 68For how these two spec JSON5 files are merged, see [Sub projects](#sub-projects) section. 69 70Note: `spec.src.json` is transitioning to JSON5 [#21710](https://github.com/web-platform-tests/wpt/issues/21710). 71 72During the generation, the spec is validated by ```common/security-features/tools/spec_validator.py```. This is specially important when you're making changes to `spec.src.json`. Make sure it's a valid JSON (no comments or trailing commas). The validator reports specific errors (missing keys etc.), if any. 73 74### Removing all generated tests 75 76Simply remove all files under `project-directory/gen/`. 77 78```bash 79rm -r path/to/project-directory/gen/ 80``` 81 82### Options for generating tests 83 84Note: this section is currently obsolete. Only the release template is working. 85 86The generator script has two targets: ```release``` and ```debug```. 87 88* Using **release** for the target will produce tests using a template for optimizing size and performance. The release template is intended for the official web-platform-tests and possibly other test suites. No sanity checking is done in release mode. Use this option whenever you're checking into web-platform-tests. 89 90* When generating for ```debug```, the produced tests will contain more verbosity and sanity checks. Use this target to identify problems with the test suites when making changes locally. Make sure you don't check in tests generated with the debug target. 91 92Note that **release** is the default target when invoking ```generate.py```. 93 94 95## Sub projects 96 97Projects can be nested, for example to reuse a single `spec.src.json` across similar but slightly different sets of generated tests. 98The directory structure would look like: 99 100``` 101project-directory/ (e.g. referrer-policy/) 102├── spec.src.json - Parent project's spec JSON 103├── generic/ 104│ └── test-case.sub.js - Parent project's test helper 105├── gen/ - parent project's generated tests 106└── sub-project-directory/ (e.g. 4K) 107 ├── spec.src.json - Child project's spec JSON 108 ├── generic/ 109 │ └── test-case.sub.js - Child project's test helper 110 └── gen/ - child project's generated tests 111``` 112 113`generate.py --spec project-directory/sub-project-directory` generates test files under `project-directory/sub-project-directory/gen`, based on `project-directory/spec.src.json` and `project-directory/sub-project-directory/spec.src.json`. 114 115- The child project's `spec.src.json` is merged into parent project's `spec.src.json`. 116 - Two spec JSON objects are merged recursively. 117 - If a same key exists in both objects, the child's value overwrites the parent's value. 118 - If both (child's and parent's) values are arrays, then the child's value is concatenated to the parent's value. 119 - For debugging, `generate.py` dumps the merged spec JSON object as `generic/debug-output.spec.src.json`. 120- The child project's generated tests include both of the parent and child project's `test-case.sub.js`: 121 ```html 122 <script src="project-directory/test-case.sub.js"></script> 123 <script src="project-directory/sub-project-directory/test-case.sub.js"></script> 124 <script> 125 TestCase(...); 126 </script> 127 ``` 128 129 130## Updating the tests 131 132The main test logic lives in ```project-directory/generic/test-case.sub.js``` with helper functions defined in ```/common/security-features/resources/common.js``` so you should probably start there. 133 134For updating the test suites you will most likely do **a subset** of the following: 135 136* Add a new subresource type: 137 138 * Add a new sub-resource python script to `/common/security-features/subresource/`. 139 * Add a sanity check test for a sub-resource to `referrer-policy/generic/subresource-test/`. 140 * Add a new entry to `subresourceMap` in `/common/security-features/resources/common.js`. 141 * Add a new entry to `valid_subresource_names` in `/common/security-features/tools/spec_validator.py`. 142 * Add a new entry to `subresource_schema` in `spec.src.json`. 143 * Update `source_context_schema` to specify in which source context the subresource can be used. 144 145* Add a new subresource redirection type 146 147 * TODO: to be documented. Example: [https://github.com/web-platform-tests/wpt/pull/18939](https://github.com/web-platform-tests/wpt/pull/18939) 148 149* Add a new subresource origin type 150 151 * TODO: to be documented. Example: [https://github.com/web-platform-tests/wpt/pull/18940](https://github.com/web-platform-tests/wpt/pull/18940) 152 153* Add a new source context (e.g. "module sharedworker global scope") 154 155 * TODO: to be documented. Example: [https://github.com/web-platform-tests/wpt/pull/18904](https://github.com/web-platform-tests/wpt/pull/18904) 156 157* Add a new source context list (e.g. "subresource request from a dedicated worker in a `<iframe srcdoc>`") 158 159 * TODO: to be documented. 160 161* Implement new or update existing assertions in ```project-directory/generic/test-case.sub.js```. 162 163* Exclude or add some tests by updating ```spec.src.json``` test expansions. 164 165* Implement a new delivery method. 166 167 * TODO: to be documented. Currently the support for delivery methods are implemented in many places across `common/security-features/`. 168 169* Regenerate the tests and MANIFEST.json 170 171## How the generator works 172 173This section describes how `spec.src.json` is turned into scenario data in test HTML files which are then processed by JavaScript test helpers and server-side scripts, and describes the objects/types used in the process. 174 175### The spec JSON 176 177`spec.src.json` is the input for the generator that defines what to generate. For examples of spec JSON files, see [referrer-policy/spec.src.json](../../referrer-policy/spec.src.json) or [mixed-content/spec.src.json](../../mixed-content/spec.src.json). 178 179#### Main sections 180 181* **`specification`** 182 183 Top level requirements with description fields and a ```test_expansion``` rule. 184 This is closely mimicking the [Referrer Policy specification](http://w3c.github.io/webappsec/specs/referrer-policy/) structure. 185 186* **`excluded_tests`** 187 188 List of ```test_expansion``` patterns expanding into selections which get skipped when generating the tests (aka. blocklisting/suppressing) 189 190* **`test_expansion_schema`** 191 192 Provides valid values for each field. 193 Each test expansion can only contain fields and values defined by this schema (or `"*"` values that indicate all the valid values defined this schema). 194 195* **`subresource_schema`** 196 197 Provides metadata of subresources, e.g. supported delivery types for each subresource. 198 199* **`source_context_schema`** 200 201 Provides metadata of each single source context, e.g. supported delivery types and subresources that can be sent from the context. 202 203* **`source_context_list_schema`** 204 205 Provides possible nested combinations of source contexts. See [SourceContexts Resolution](#sourcecontexts-resolution) section below for details. 206 207### Test Expansion Pattern Object 208 209Test expansion patterns (`test_expansion`s in `specification` section) define the combinations of test configurations (*selections*) to be generated. 210Each field in a test expansion can be in one of the following formats: 211 212* Single match: ```"value"``` 213 214* Match any of: ```["value1", "value2", ...]``` 215 216* Match all: ```"*"``` 217 218The following fields have special meaning: 219 220- **`name`**: just ignored. (Previously this was used as a part of filenames but now this is merely a label for human and is never used by generator. This field might be removed in the future (https://github.com/web-platform-tests/wpt/issues/21708)) 221- **`expansion`**: if there is more than one pattern expanding into a same selection, the pattern appearing later in the spec JSON will overwrite a previously generated selection. To make clear this is intentional, set the value of the ```expansion``` field to ```default``` for an expansion appearing earlier and ```override``` for the one appearing later. 222 223For example a test expansion pattern (taken from [referrer-policy/spec.src.json](../../referrer-policy/spec.src.json), sorted/formatted for explanation): 224 225```json 226{ 227 "name": "insecure-protocol", 228 "expansion": "default", 229 230 "delivery_type": "*", 231 "delivery_value": "no-referrer-when-downgrade", 232 "source_context_list": "*", 233 234 "expectation": "stripped-referrer", 235 "origin": ["same-http", "cross-http"], 236 "redirection": "*", 237 "source_scheme": "http", 238 "subresource": "*" 239} 240``` 241 242means: "All combinations with all possible `delivery_type`, `delivery_value`=`no-referrer-when-downgrade`, all possible `source_context_list`, `expectation`=`stripped-referrer`, `origin`=`same-http` or `cross-http`, all possible `redirection`, `source_scheme`=`http`, and all possible `subresource`. 243 244### Selection Object 245 246A selection is an object that defines a single test, with keys/values from `test_expansion_schema`. 247 248A single test expansion pattern gets expanded into a list of selections as follows: 249 250* Expand each field's pattern (single, any of, or all) to list of allowed values (defined by the ```test_expansion_schema```) 251 252* Permute - Recursively enumerate all selections across all fields 253 254The following field has special meaning: 255 256- **`delivery_key`**: This doesn't exist in test expansion patterns, and instead is taken from `delivery_key` field of the spec JSON and added into selections. (TODO(https://github.com/web-platform-tests/wpt/issues/21708): probably this should be added to test expansion patterns to remove this special handling) 257 258For example, the test expansion in the example above generates selections like the following selection (which eventually generates [this test file](../../referrer-policy/gen/worker-classic.http-rp/no-referrer-when-downgrade/fetch/same-http.no-redirect.http.html )): 259 260```json 261{ 262 "delivery_type": "http-rp", 263 "delivery_key": "referrerPolicy", 264 "delivery_value": "no-referrer-when-downgrade", 265 "source_context_list": "worker-classic", 266 267 "expectation": "stripped-referrer", 268 "origin": "same-http", 269 "redirection": "no-redirect", 270 "source_scheme": "http", 271 "subresource": "fetch" 272} 273``` 274 275### Excluding Test Expansion Patterns 276 277The ```excluded_tests``` section have objects with the same format as [Test Expansion Patterns](#test-expansion-patterns) that define selections to be excluded. 278 279Taking the spec JSON, the generator follows this algorithm: 280 281* Expand all ```excluded_tests``` to create a denylist of selections 282 283* For each `specification` entries: Expand the ```test_expansion``` pattern into selections and check each against the denylist, if not marked as suppresed, generate the test resources for the selection 284 285### SourceContext Resolution 286 287The `source_context_list_schema` section of `spec.src.json` defines templates of policy deliveries and source contexts. 288The `source_context_list` value in a selection specifies the key of the template to be used in `source_context_list_schema`, and the following fields in the selection are filled into the template (these three values define the **target policy delivery** to be tested): 289 290- `delivery_type` 291- `delivery_key` 292- `delivery_value` 293 294#### Source Context List Schema 295 296Each entry of **`source_context_list_schema`**, defines a single template of how/what policies to be delivered in what source contexts (See also [PolicyDelivery](types.md#policydelivery) and [SourceContext](types.md#sourcecontext)). 297 298- The key: the name of the template which matches with the `source_context_list` value in a selection. 299- `sourceContextList`: an array of `SourceContext` objects that represents a (possibly nested) context. 300 - `sourceContextType` of the first entry of `sourceContextList` should be always `"top"`, which represents the top-level generated test HTML. This entry is omitted in the scenario JSON object passed to JavaScript runtime, but the policy deliveries specified here are handled by the generator, e.g. written as `<meta>` elements in the generated test HTML. 301- `subresourcePolicyDeliveries`: an array of `PolicyDelivery` objects that represents policies specified at subresource requests (e.g. `referrerPolicy` attribute of `<img>` elements). 302 303#### PolicyDelivery placeholders 304 305Instead to ordinal `PolicyDelivery` objects, the following placeholder strings can be used in `sourceContextList` or `subresourcePolicyDeliveries`. 306 307- `"policy"`: 308 - Replaced with the target policy delivery. 309- `"policyIfNonNull"`: 310 - Replaced with the target policy delivery, only if `delivery_value` is not `null`. 311 If `delivery_value` is `null`, then the test is not generated. 312- `"anotherPolicy"`: 313 - Replaced with a `PolicyDelivery` object that has a different value from 314 the target policy delivery. 315 - Can be used to specify e.g. a policy that should be overridden by 316 the target policy delivery. 317 318#### `source_context_schema` and `subresource_schema` 319 320These represent supported delivery types and subresources 321for each source context or subresource type. These are used 322 323- To filter out test files for unsupported combinations of delivery types, 324 source contexts and subresources during SourceContext resolution. 325- To determine what delivery types can be used for `anotherPolicy` 326 placeholder. 327 328#### Example 329 330For example, the following entry in `source_context_list_schema`: 331 332```json 333"worker-classic": { 334 "sourceContextList": [ 335 { 336 "sourceContextType": "top", 337 "policyDeliveries": [ 338 "anotherPolicy" 339 ] 340 }, 341 { 342 "sourceContextType": "worker-classic", 343 "policyDeliveries": [ 344 "policy" 345 ] 346 } 347 ], 348 "subresourcePolicyDeliveries": [] 349} 350``` 351 352Defines a template to be instantiated with `delivery_key`, `delivery_type` and `delivery_value` values defined outside `source_context_list_schema`, which reads: 353 354- A classic `WorkerGlobalScope` is created under the top-level Document, and has a policy defined by `delivery_key`, `delivery_type` and `delivery_value`. 355- The top-level Document has a policy different from the policy given to the classic worker (to confirm that the policy of the classic worker, not of the top-level Document, is used). 356- The subresource request is sent from the classic `WorkerGlobalScope`, with no additional policies specified at the subresource request. 357 358And when filled with the following values from a selection: 359 360- `delivery_type`: `"http-rp"` 361- `delivery_key`: `"referrerPolicy"` 362- `delivery_value`: `"no-referrer-when-downgrade"` 363 364This becomes: 365 366```json 367"worker-classic": { 368 "sourceContextList": [ 369 { 370 "sourceContextType": "top", 371 "policyDeliveries": [ 372 { 373 "deliveryType": "meta", 374 "key": "referrerPolicy", 375 "value": "no-referrer" 376 } 377 ] 378 }, 379 { 380 "sourceContextType": "worker-classic", 381 "policyDeliveries": [ 382 { 383 "deliveryType": "http-rp", 384 "key": "referrerPolicy", 385 "value": "no-referrer-when-downgrade" 386 } 387 ] 388 } 389 ], 390 "subresourcePolicyDeliveries": [] 391} 392``` 393 394which means 395 396- The top-level Document has `<meta name="referrer" content="no-referrer">`. 397- The classic worker is created with 398 `Referrer-Policy: no-referrer-when-downgrade` HTTP response headers. 399 400### Scenario Object 401 402The **scenario** object is the JSON object written to the generated HTML files, and passed to JavaScript test runtime (as an argument of `TestCase`). 403A scenario object is an selection object, minus the keys used in [SourceContext Resolution](#sourceContext-resolution): 404 405- `source_context_list` 406- `delivery_type` 407- `delivery_key` 408- `delivery_value` 409 410plus the keys instantiated by [SourceContext Resolution](#sourceContext-resolution): 411 412- `source_context_list`, except for the first `"top"` entry. 413- `subresource_policy_deliveries` 414 415For example: 416 417```json 418{ 419 "source_context_list": [ 420 { 421 "sourceContextType": "worker-classic", 422 "policyDeliveries": [ 423 { 424 "deliveryType": "http-rp", 425 "key": "referrerPolicy", 426 "value": "no-referrer-when-downgrade" 427 } 428 ] 429 } 430 ], 431 "subresource_policy_deliveries": [], 432 433 "expectation": "stripped-referrer", 434 "origin": "same-http", 435 "redirection": "no-redirect", 436 "source_scheme": "http", 437 "subresource": "fetch" 438} 439``` 440 441### TopLevelPolicyDelivery Object 442 443The ***TopLevelPolicyDelivery** object is the first `"top"` entry of `SourceContextList` instantiated by [SourceContext Resolution](#sourceContext-resolution), which represents the policy delivery of the top-level HTML Document. 444 445The generator generates `<meta>` elements and `.headers` files of the top-level HTML files from the TopLevelPolicyDelivery object. 446 447This is handled separately by the generator from other parts of selection objects and scenario objects, because the `<meta>` and `.headers` are hard-coded directly to the files in the WPT repository, while policies of subcontexts are generated via server-side `common/security-features/scope` scripts. 448 449TODO(https://github.com/web-platform-tests/wpt/issues/21710): Currently the name `TopLevelPolicyDelivery` doesn't appear in the code. 450 451## How the test runtime works 452 453All the information needed at runtime is contained in an scenario object. See the code/comments of the following files. 454 455- `project-directory/generic/test-case.js` defines `TestCase`, the entry point that receives a scenario object. `resources/common.sub.js` does the most of common JavaScript work. 456 - Subresource URLs (which point to `subresource/` scripts) are calculated from `origin` and `redirection` values. 457 - Initiating fetch requests based on `subresource` and `subresource_policy_deliveries`. 458- `scope/` server-side scripts serve non-toplevel contexts, while the top-level Document is generated by the generator. 459 TODO(https://github.com/web-platform-tests/wpt/issues/21709): Merge the logics of `scope/` and the generator. 460- `subresource/` server-side scripts serve subresource responses. 461