Do Entity Field Queries respect grants and/or node access?
EntityFieldQuery will always check the user has the permission to access the field, if "DANGEROUS_ACCESS_CHECK_OPT_OUT" is not between the tags added to the query, but that is not done directly. It is done by adding a tag to the query, as field_sql_storage_field_storage_query() does.

if (!isset($query-&gt;tags['DANGEROUS_ACCESS_CHECK_OPT_OUT'])) {
  $select_query-&gt;addTag('entity_field_access');
}


It is then an implementation of hook_query_TAG_alter() that alters the query to check the permission. The Node module implement that hook with node_query_entity_field_access_alter().

function node_query_entity_field_access_alter(QueryAlterableInterface $query) {
  _node_query_node_access_alter($query, 'entity');
}


That is the reason why, before Drupal 7.15 (which introduced the "DANGEROUS_ACCESS_CHECK_OPT_OUT" tag), it was suggested to use $query-&gt;addMetaData('account', user_load(1)). The user #1 always have any permission, and checking the permission to access a field would be always positive.

_node_query_node_access_alter() contains the following code.

  // If $account can bypass node access, or there are no node access modules,
  // or the operation is 'view' and the $acount has a global view grant (i.e.,
  // a view grant for node ID 0), we don't need to alter the query.
  if (user_access('bypass node access', $account)) {
    return;
  }
  if (!count(module_implements('node_grants'))) {
    return;
  }
  if ($op == 'view' &amp;&amp; node_access_view_all_nodes($account)) {
    return;
  }

  // Omissis

  // Find all instances of the base table being joined -- could appear
  // more than once in the query, and could be aliased. Join each one to
  // the node_access table.

  $grants = node_access_grants($op, $account);
  if ($type == 'entity') {
    // The original query looked something like:
    // @code
    //  SELECT nid FROM sometable s
    //  INNER JOIN node_access na ON na.nid = s.nid
    //  WHERE ($node_access_conditions)
    // @endcode
    //
    // Our query will look like:
    // @code
    //  SELECT entity_type, entity_id
    //  FROM field_data_something s
    //  LEFT JOIN node_access na ON s.entity_id = na.nid
    //  WHERE (entity_type = 'node' AND $node_access_conditions) OR (entity_type &lt;&gt; 'node')
    // @endcode
    //
    // So instead of directly adding to the query object, we need to collect
    // all of the node access conditions in a separate db_and() object and
    // then add it to the query at the end.
    $node_conditions = db_and();
  }
  foreach ($tables as $nalias =&gt; $tableinfo) {
    $table = $tableinfo['table'];
    if (!($table instanceof SelectQueryInterface) &amp;&amp; $table == $base_table) {
      // Set the subquery.
      $subquery = db_select('node_access', 'na')
       -&gt;fields('na', array('nid'));

      $grant_conditions = db_or();
      // If any grant exists for the specified user, then user has access
      // to the node for the specified operation.
      foreach ($grants as $realm =&gt; $gids) {
        foreach ($gids as $gid) {
          $grant_conditions-&gt;condition(db_and()
            -&gt;condition('na.gid', $gid)
            -&gt;condition('na.realm', $realm)
          );
        }
      }


Definitively, the code checks the user has access to the node.