Uma regra de service policy pode ter vários predicados ao mesmo tempo: um path, um método, alguns headers, um client selector, uma lista de IPs. A lógica que junta tudo isso é simples de explicar e fácil de errar, porque funciona em dois níveis.

AND entre predicados

Todos os predicados que você define numa regra precisam ser verdadeiros para a regra casar: eles são combinados com AND (E). Uma regra com um predicado de path e um predicado de método só casa com uma requisição que atenda aos dois.

Tão importante quanto isso: qualquer predicado que você não define é implicitamente verdadeiro. Uma regra que só tem o predicado http_method não impõe nenhuma restrição de path, de headers ou de origem; ela casa com qualquer requisição que use aquele método. É por isso que uma regra sem nenhum predicado casa com tudo, e é por isso que acrescentar predicados sempre restringe a regra, nunca a amplia.

OR dentro de um matcher

A maioria dos matchers (os comparadores) guarda uma lista de valores, e dentro dessa lista a lógica é OR (OU). Um matcher de path com três prefix_values casa com um path que comece com qualquer um deles. Um item de header com três exact_values casa se o header for igual a qualquer um deles. Uma lista http_method com ["GET", "POST"] casa com qualquer um dos dois métodos.

Então o modelo mental é este: OR dentro de um campo, AND entre campos.

A armadilha dos vários matchers

Onde isso pega as pessoas é nos headers (e também nos parâmetros de query e nos cookies), porque cada um deles é uma lista de objetos matcher, e colocar vários valores num matcher não é a mesma coisa que colocar vários matchers na lista.

headers:
  - name: partner-name
    item: { exact_values: ["GOOGLE"] }
  - name: User-Agent
    item: { exact_values: ["GoogleMobile-9.1.76", "GoogleMobile-9.1.77"] }

As duas entradas de header são combinadas com AND: a requisição precisa trazer um partner-name que case e um User-Agent que case. Mas, dentro do matcher de User-Agent, os dois valores são OR: qualquer uma das duas strings de user-agent serve.

Uma armadilha parecida: uma string de valor de header é comparada ao pé da letra, com vírgula e tudo. Se você escreve uma única entrada em exact_values com "GOOGLE,BING", achando que isso significa dois parceiros, ela só vai casar com um header cujo valor seja exatamente os onze caracteres GOOGLE,BING. Para casar com qualquer um dos dois, você precisa de duas entradas em exact_values, e não de uma string com os nomes separados por vírgula.

Presença, ausência e inversão

Os matchers de header, de cookie, de argumento e de claim de (JSON Web Token) também aceitam check_present e check_not_present, que só testam se o campo está presente ou não, sem comparar valor nenhum. E a maioria dos matchers aceita invert_matcher, que inverte o resultado: o predicado casa quando a condição não é atendida. Um matcher de método invertido com ["GET"] casa com qualquer método, menos GET. A inversão é poderosa e fácil de ler errado, então vale a pena destacá-la sempre que ela aparecer.

O explainer de service policy deste site mostra cada predicado com os seus valores, marca as listas OR e destaca os matchers invertidos, e assim os dois níveis de lógica ficam visíveis de imediato, em vez de terem de ser deduzidos pelo aninhamento.

Um predicado que nunca casa parece igual a um que sempre permite

Os dois níveis de lógica (AND entre predicados, OR dentro de um matcher) fazem com que uma regra possa ficar mais restrita do que você queria sem dar erro nenhum. Um nome de header escrito errado, um prefixo de path sem a barra inicial, um selector cujo label não existe mais: cada um desses erros torna o predicado impossível de satisfazer, e um predicado impossível de satisfazer numa regra ALLOW significa, simplesmente, que a regra nunca dispara.

O tráfego então segue para o que vier depois. A política está aplicando alguma coisa, então nada parece quebrado, e, se o que vem depois for permissivo, a regra que você escreveu para restringir a requisição não contribui com nada, embora continue aparecendo na configuração.

Teste se a regra casa com o que deve e, também, se ela casa com alguma coisa. Uma regra com zero hits depois de um dia de tráfego ou é desnecessária ou está errada, e a configuração não tem como dizer qual das duas.