Repository: graphql/graphql-js
Pull request #3751 (2022-09-26 12:15:09)

GitHub Workflows security hardening
This PR adds explicit [permissions section](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions) to workflows. This is a security best practice because by default workflows run with [extended set of permissions](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token) (except from `on: pull_request` [from external forks](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)). By specifying any permission explicitly all others are set to none. By using the principle of least privilege the damage a compromised workflow can do (because of an [injection](https://securitylab.github.com/research/github-actions-untrusted-input/) or compromised third party tool or action) is restricted.
It is recommended to have [most strict permissions on the top level](https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions) and grant write permissions on [job level](https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs) case by case.


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ba710270..47965ee2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,5 +1,7 @@
 name: CI
 on: workflow_call
+permissions:
+  contents: read
 jobs:
   lint:
     name: Lint source files
@@ -158,6 +160,7 @@ jobs:
     name: Run CodeQL security scan
     runs-on: ubuntu-latest
     permissions:
+      contents: read
       security-events: write
     steps:
       - name: Checkout repo
diff --git a/.github/workflows/cmd-publish-pr-on-npm.yml b/.github/workflows/cmd-publish-pr-on-npm.yml
index b35ca895..cf14b2e6 100644
--- a/.github/workflows/cmd-publish-pr-on-npm.yml
+++ b/.github/workflows/cmd-publish-pr-on-npm.yml
@@ -10,6 +10,8 @@ on:
       npm_canary_pr_publish_token:
         description: NPM token to publish canary release.
         required: true
+permissions:
+  contents: read
 jobs:
   build-npm-dist:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/cmd-run-benchmark.yml b/.github/workflows/cmd-run-benchmark.yml
index 7f8d5764..1d2edddc 100644
--- a/.github/workflows/cmd-run-benchmark.yml
+++ b/.github/workflows/cmd-run-benchmark.yml
@@ -6,6 +6,9 @@ on:
         description: String that contain JSON payload for `pull_request` event.
         required: true
         type: string
+permissions:
+  contents: read # for checkout
+  actions: read # to list workflow runs
 jobs:
   benchmark:
     name: Run benchmark
diff --git a/.github/workflows/deploy-artifact-as-branch.yml b/.github/workflows/deploy-artifact-as-branch.yml
index 4dd7e1b5..df8385e0 100644
--- a/.github/workflows/deploy-artifact-as-branch.yml
+++ b/.github/workflows/deploy-artifact-as-branch.yml
@@ -18,8 +18,11 @@ on:
         description: Commit message
         required: true
         type: string
+permissions: {}
 jobs:
   deploy-artifact-as-branch:
+    permissions:
+      contents: write # to push branch
     environment:
       name: ${{ inputs.environment }}
       url: ${{ github.server_url }}/${{ github.repository }}/tree/${{ inputs.target_branch }}
diff --git a/.github/workflows/github-actions-bot.yml b/.github/workflows/github-actions-bot.yml
index 6f9b48f2..d83524c0 100644
--- a/.github/workflows/github-actions-bot.yml
+++ b/.github/workflows/github-actions-bot.yml
@@ -21,8 +21,13 @@ env:
     * `@github-actions run-benchmark` - Run benchmark comparing base and merge commits for this PR
     * `@github-actions publish-pr-on-npm` - Build package from this PR and publish it on NPM
     </details>
+permissions: {}
 jobs:
   hello-message:
+    permissions:
+      actions: read # to download event.json
+      pull-requests: write # to add comment to pull request
+
     if: github.event_name == 'workflow_run'
     runs-on: ubuntu-latest
     steps:
@@ -49,6 +54,9 @@ jobs:
             })
 
   accept-cmd:
+    permissions:
+      pull-requests: write # to add comment to pull request
+
     if: |
       github.event_name == 'issue_comment' &&
       github.event.issue.pull_request &&
@@ -95,6 +103,9 @@ jobs:
       pull_request_json: ${{ needs.accept-cmd.outputs.pull_request_json }}
 
   respond-to-cmd:
+    permissions:
+      pull-requests: write # to add comment to pull request
+
     needs:
       - accept-cmd
       - cmd-publish-pr-on-npm
diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml
index 30e42d13..1d2026c3 100644
--- a/.github/workflows/push.yml
+++ b/.github/workflows/push.yml
@@ -1,5 +1,6 @@
 name: Push
 on: push
+permissions: {}
 jobs:
   ci:
     uses: ./.github/workflows/ci.yml
diff --git a/package-lock.json b/package-lock.json
index baf92e41..d4f73bf2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "graphql",
-  "version": "17.0.0-alpha.1",
+  "version": "17.0.0-alpha.2",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "graphql",
-      "version": "17.0.0-alpha.1",
+      "version": "17.0.0-alpha.2",
       "license": "MIT",
       "devDependencies": {
         "@docusaurus/core": "2.1.0",
diff --git a/package.json b/package.json
index 6ee15e96..5c2ce7ba 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "graphql",
-  "version": "17.0.0-alpha.1",
+  "version": "17.0.0-alpha.2",
   "description": "A Query Language and Runtime which can target any service.",
   "license": "MIT",
   "private": true,
diff --git a/src/execution/execute.ts b/src/execution/execute.ts
index 37eed9e1..486a80fe 100644
--- a/src/execution/execute.ts
+++ b/src/execution/execute.ts
@@ -1017,8 +1017,22 @@ async function completeAsyncIteratorValue(
         );
         if (isPromise(completedItem)) {
           containsPromise = true;
+          // Note: we don't rely on a `catch` method, but we do expect "thenable"
+          // to take a second callback for the error case.
+          completedResults.push(
+            completedItem.then(undefined, (rawError) => {
+              const error = locatedError(
+                rawError,
+                fieldNodes,
+                pathToArray(fieldPath),
+              );
+              const handledError = handleFieldError(error, itemType, errors);
+              return handledError;
+            }),
+          );
+        } else {
+          completedResults.push(completedItem);
         }
-        completedResults.push(completedItem);
       } catch (rawError) {
         completedResults.push(null);
         const error = locatedError(
diff --git a/src/version.ts b/src/version.ts
index 70e7ed00..67a5755c 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -4,7 +4,7 @@
 /**
  * A string containing the version of the GraphQL.js library
  */
-export const version = '17.0.0-alpha.1' as string;
+export const version = '17.0.0-alpha.2' as string;
 
 /**
  * An object containing the components of the GraphQL.js version string
@@ -13,5 +13,5 @@ export const versionInfo = Object.freeze({
   major: 17 as number,
   minor: 0 as number,
   patch: 0 as number,
-  preReleaseTag: 'alpha.1' as string | null,
+  preReleaseTag: 'alpha.2' as string | null,
 });


Test patch:
diff --git a/.github/workflows/mutation-testing.yml b/.github/workflows/mutation-testing.yml
index 7e60924d..881ab348 100644
--- a/.github/workflows/mutation-testing.yml
+++ b/.github/workflows/mutation-testing.yml
@@ -3,6 +3,10 @@ on:
   workflow_dispatch:
   schedule:
     - cron: '0 0 * * *' # run once every day at 00:00 UTC
+
+permissions:
+  contents: read # to fetch code (actions/checkout)
+
 jobs:
   lint:
     name: Run mutation testing
diff --git a/src/execution/__tests__/lists-test.ts b/src/execution/__tests__/lists-test.ts
index 32f954c5..1df9be75 100644
--- a/src/execution/__tests__/lists-test.ts
+++ b/src/execution/__tests__/lists-test.ts
@@ -8,7 +8,11 @@ import type { PromiseOrValue } from '../../jsutils/PromiseOrValue.js';
 import { parse } from '../../language/parser.js';
 
 import type { GraphQLFieldResolver } from '../../type/definition.js';
-import { GraphQLList, GraphQLObjectType } from '../../type/definition.js';
+import {
+  GraphQLList,
+  GraphQLNonNull,
+  GraphQLObjectType,
+} from '../../type/definition.js';
 import { GraphQLString } from '../../type/scalars.js';
 import { GraphQLSchema } from '../../type/schema.js';
 
@@ -101,7 +105,7 @@ describe('Execute: Accepts async iterables as list value', () => {
                 name: 'ObjectWrapper',
                 fields: {
                   index: {
-                    type: GraphQLString,
+                    type: new GraphQLNonNull(GraphQLString),
                     resolve,
                   },
                 },
@@ -202,7 +206,7 @@ describe('Execute: Accepts async iterables as list value', () => {
         return Promise.resolve(index);
       }),
     ).toDeepEqual({
-      data: { listField: [{ index: '0' }, { index: '1' }, { index: null }] },
+      data: { listField: [{ index: '0' }, { index: '1' }, null] },
       errors: [
         {
           message: 'bad',