CORS _additionally_ requires OPTIONS pre-flight to succeed, before allowing any kind of request outside of what can be achieved with a HTML form submit action. So it blocks PUT/PATCH/DELETE, specifying most Content-Type, and specifying nearly all other headers. But this is just blocking "non-standard complex requests that might confuse badly programmed pre-javascript-era servers".
It passes all standard requests that you could have made by: embedding the url as an image src, the target of a HTML form, endpoint for csp reports, etc. All still need to be checked methodically by the server for CSRF if it's going to take any mutating action due to the request.
The browser enforces the same-origin policy by preventing read on non-mutating (i.e. “simple”) request responses and preventing sending of mutating requests (i.e. non-“simple”). CORS provides a protocol for a service to loosen these controls.
It does block ALL requests for certain content types.
In the common cross origin case of a JSON API, CSRF beyond CORS is unnecessary.
To nitpick, it’s the same origin policy that does that.
Assuming the web client plays nicely. I commonly bypass CORS for playwright unit/E2E testing.