diff --git a/UPGRADING.md b/UPGRADING.md
index 9b3665d4d..767e29387 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -17,7 +17,7 @@
- [BC] Class `Psalm\Issue\MixedInferredReturnType` was removed
-- [BC] Value of constant `Psalm\Type\TaintKindGroup::ALL_INPUT` changed to reflect new `TaintKind::INPUT_SLEEP` and `TaintKind::INPUT_XPATH` have been added. Accordingly, default values for `$taint` parameters of `Psalm\Codebase::addTaintSource()` and `Psalm\Codebase::addTaintSink()` have been changed as well.
+- [BC] Value of constant `Psalm\Type\TaintKindGroup::ALL_INPUT` changed to reflect new `TaintKind::INPUT_EXTRACT`, `TaintKind::INPUT_SLEEP` and `TaintKind::INPUT_XPATH` have been added. Accordingly, default values for `$taint` parameters of `Psalm\Codebase::addTaintSource()` and `Psalm\Codebase::addTaintSink()` have been changed as well.
- [BC] Property `Config::$shepherd_host` was replaced with `Config::$shepherd_endpoint`
diff --git a/config.xsd b/config.xsd
index 6a6d182dc..c97e3198a 100644
--- a/config.xsd
+++ b/config.xsd
@@ -433,6 +433,7 @@
+
diff --git a/docs/running_psalm/error_levels.md b/docs/running_psalm/error_levels.md
index 2d9c35ced..a7b61ee78 100644
--- a/docs/running_psalm/error_levels.md
+++ b/docs/running_psalm/error_levels.md
@@ -286,6 +286,7 @@ Level 5 and above allows a more non-verifiable code, and higher levels are even
- [TaintedCookie](issues/TaintedCookie.md)
- [TaintedCustom](issues/TaintedCustom.md)
- [TaintedEval](issues/TaintedEval.md)
+ - [TaintedExtract](issues/TaintedExtract.md)
- [TaintedFile](issues/TaintedFile.md)
- [TaintedHeader](issues/TaintedHeader.md)
- [TaintedHtml](issues/TaintedHtml.md)
diff --git a/docs/running_psalm/issues.md b/docs/running_psalm/issues.md
index 95f383959..364541ee4 100644
--- a/docs/running_psalm/issues.md
+++ b/docs/running_psalm/issues.md
@@ -234,6 +234,7 @@
- [TaintedCookie](issues/TaintedCookie.md)
- [TaintedCustom](issues/TaintedCustom.md)
- [TaintedEval](issues/TaintedEval.md)
+ - [TaintedExtract](issues/TaintedExtract.md)
- [TaintedFile](issues/TaintedFile.md)
- [TaintedHeader](issues/TaintedHeader.md)
- [TaintedHtml](issues/TaintedHtml.md)
diff --git a/docs/running_psalm/issues/TaintedExtract.md b/docs/running_psalm/issues/TaintedExtract.md
new file mode 100644
index 000000000..7b0fa27d8
--- /dev/null
+++ b/docs/running_psalm/issues/TaintedExtract.md
@@ -0,0 +1,10 @@
+# TaintedExtract
+
+Emitted when user-controlled array can be passed into an `extract` call.
+
+```php
+vars_in_scope[$var_id]))
diff --git a/src/Psalm/Internal/Codebase/TaintFlowGraph.php b/src/Psalm/Internal/Codebase/TaintFlowGraph.php
index 5c5f72173..1cab3ea6d 100644
--- a/src/Psalm/Internal/Codebase/TaintFlowGraph.php
+++ b/src/Psalm/Internal/Codebase/TaintFlowGraph.php
@@ -14,6 +14,7 @@ use Psalm\Issue\TaintedCallable;
use Psalm\Issue\TaintedCookie;
use Psalm\Issue\TaintedCustom;
use Psalm\Issue\TaintedEval;
+use Psalm\Issue\TaintedExtract;
use Psalm\Issue\TaintedFile;
use Psalm\Issue\TaintedHeader;
use Psalm\Issue\TaintedHtml;
@@ -471,6 +472,15 @@ final class TaintFlowGraph extends DataFlowGraph
);
break;
+ case TaintKind::INPUT_EXTRACT:
+ $issue = new TaintedExtract(
+ 'Detected tainted extract',
+ $issue_location,
+ $issue_trace,
+ $path,
+ );
+ break;
+
default:
$issue = new TaintedCustom(
'Detected tainted ' . $matching_taint,
diff --git a/src/Psalm/Issue/TaintedExtract.php b/src/Psalm/Issue/TaintedExtract.php
new file mode 100644
index 000000000..60eef6b92
--- /dev/null
+++ b/src/Psalm/Issue/TaintedExtract.php
@@ -0,0 +1,10 @@
+ 'TaintedSleep',
],
+ 'taintedExtract' => [
+ 'code' => ' 'TaintedExtract',
+ ],
+ 'extractPost' => [
+ 'code' => ' 'TaintedExtract',
+ ],
];
}