forked from mirrors/kingfisher
Fixed malformed rules. Now validating that response_matcher is present in validation section of all rules
This commit is contained in:
parent
e7e391ab98
commit
0d3513b6f9
18 changed files with 152 additions and 101 deletions
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [1.13.1]
|
||||
- Fixed broken pagerduty rule
|
||||
## [1.14.0]
|
||||
- Fixed several malformed rules
|
||||
- Now validating that response_matcher is present in validation section of all rules
|
||||
|
||||
## [1.13.0]
|
||||
- Added new rules for Planetscale, Postman, Openweather, opsgenie, pagerduty, pastebin, paypal, netlify, netrc, newrelic, ngrok, npm, nuget, mandrill, mapbox, microsoft teams, stripe, linkedin, mailchimp, mailgun, linear, line, huggingface, ibm cloud, intercom, ipstack, heroku, gradle, grafana
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ publish = false
|
|||
|
||||
[package]
|
||||
name = "kingfisher"
|
||||
version = "1.13.1"
|
||||
version = "1.14.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
|||
|
|
@ -60,9 +60,9 @@ rules:
|
|||
"grant_type": "refresh_token",
|
||||
"refresh_token": "{{ TOKEN }}"
|
||||
}
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
- type: JsonValid
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
- type: JsonValid
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ rules:
|
|||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
- name: Doppler Personal Token
|
||||
id: kingfisher.doppler.2
|
||||
pattern: |
|
||||
|
|
@ -52,11 +52,11 @@ rules:
|
|||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
|
||||
- name: Doppler Service Token
|
||||
id: kingfisher.doppler.3
|
||||
|
|
@ -82,11 +82,11 @@ rules:
|
|||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
|
||||
- name: Doppler Service Account Token
|
||||
id: kingfisher.doppler.4
|
||||
|
|
@ -112,11 +112,11 @@ rules:
|
|||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
|
||||
- name: Doppler SCIM Token
|
||||
id: kingfisher.doppler.5
|
||||
|
|
@ -142,11 +142,11 @@ rules:
|
|||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
|
||||
- name: Doppler Audit Token
|
||||
id: kingfisher.doppler.6
|
||||
|
|
@ -172,8 +172,8 @@ rules:
|
|||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
|
|
@ -23,6 +23,12 @@ rules:
|
|||
X-Figma-Token: '{{ TOKEN }}'
|
||||
method: GET
|
||||
url: https://api.figma.com/v1/me
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: WordMatch
|
||||
words:
|
||||
- "Invalid token"
|
||||
negative: true
|
||||
|
||||
- name: Figma Personal Access Header Token
|
||||
id: kingfisher.figma.2
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@ rules:
|
|||
id: kingfisher.ibm.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(?:ibm(?:cloud)?|bx)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[0-9A-Z_-]{42,44}
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ rules:
|
|||
"query": "query { issues(first: 1) { nodes { id } } }"
|
||||
}
|
||||
url: https://api.linear.app/graphql
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: WordMatch
|
||||
words: ['"issues":', '"nodes":']
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: WordMatch
|
||||
words: ['"issues":', '"nodes":']
|
||||
|
|
@ -42,11 +42,11 @@ rules:
|
|||
headers:
|
||||
Content-Type: application/json
|
||||
body: '{"text":""}'
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 400
|
||||
- type: WordMatch
|
||||
words:
|
||||
- 'Text is required'
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 400
|
||||
- type: WordMatch
|
||||
words:
|
||||
- 'Text is required'
|
||||
|
|
@ -12,9 +12,10 @@ rules:
|
|||
pagerduty[_-]? |
|
||||
pagerduty
|
||||
)
|
||||
(?:.|[\n\r]){0,16}?
|
||||
\W{0,20}
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,16}?
|
||||
(?:.|[\n\r]){0,16}?
|
||||
\b
|
||||
(
|
||||
u\+[A-Z0-9_+-]{18} | # personal user token (20 chars)
|
||||
[A-Z0-9_-]{20} | # legacy PAT (20 chars, mixed case)
|
||||
|
|
@ -28,7 +29,7 @@ rules:
|
|||
- pd_key = u+3xVszZ-b4m+T6d23KA
|
||||
- Token token=ABCDEF1234567890ABCDEF1234567890
|
||||
references:
|
||||
- https://developer.pagerduty.com/api-reference/c96e889522dd6-list-users
|
||||
- https://developer.pagerduty.com/api-reference/4555ca1c983d0-get-the-current-user
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
|
|
@ -37,11 +38,10 @@ rules:
|
|||
url: https://api.pagerduty.com/users
|
||||
headers:
|
||||
Authorization: Token token={{ TOKEN }}
|
||||
Accept: application/vnd.pagerduty+json;version=2
|
||||
Content-Type: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: WordMatch
|
||||
words: ['"user":']
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: JsonValid
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"users":'
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ rules:
|
|||
request:
|
||||
method: GET
|
||||
url: https://api.particle.io/v1/user?access_token={{ TOKEN }}
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: WordMatch
|
||||
match_all_words: true
|
||||
words: ['"username":']
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: WordMatch
|
||||
match_all_words: true
|
||||
words: ['"username":']
|
||||
|
||||
- name: particle.io Access Token
|
||||
id: kingfisher.particleio.2
|
||||
|
|
@ -65,10 +65,10 @@ rules:
|
|||
request:
|
||||
method: GET
|
||||
url: https://api.particle.io/v1/user?access_token={{ TOKEN }}
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: WordMatch
|
||||
match_all_words: true
|
||||
words: ['"username":']
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: WordMatch
|
||||
match_all_words: true
|
||||
words: ['"username":']
|
||||
|
|
@ -28,10 +28,10 @@ rules:
|
|||
Content-Type: application/x-www-form-urlencoded
|
||||
body: |
|
||||
api_dev_key={{ TOKEN }}&api_user_name=dummy&api_user_password=dummy
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: WordMatch
|
||||
words: ['invalid api_dev_key']
|
||||
negative: true
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: WordMatch
|
||||
words: ['invalid api_dev_key']
|
||||
negative: true
|
||||
|
|
@ -47,10 +47,10 @@ rules:
|
|||
Authorization: |
|
||||
Basic {{ CLIENTID | append: ':' | append: TOKEN | b64enc }}
|
||||
body: grant_type=client_credentials
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
depends_on_rule:
|
||||
- rule_id: kingfisher.paypal.1
|
||||
variable: CLIENTID
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ rules:
|
|||
headers:
|
||||
Authorization: "Bearer {{ TOKEN }}"
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ rules:
|
|||
Authorization: token {{ TOKEN }}
|
||||
Accept: application/vnd.travis-ci.3+json
|
||||
Travis-API-Version: "3"
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
|
|
|
|||
|
|
@ -981,7 +981,7 @@ mod test {
|
|||
method: "GET".to_string(),
|
||||
url: "https://example.com".to_string(),
|
||||
headers: BTreeMap::new(),
|
||||
response_matcher: vec![],
|
||||
response_matcher: Some(vec![]),
|
||||
multipart: None,
|
||||
response_is_html: false,
|
||||
},
|
||||
|
|
|
|||
20
src/rules.rs
20
src/rules.rs
|
|
@ -8,7 +8,7 @@ pub mod rule;
|
|||
use std::{fs::File, io::BufReader, path::Path};
|
||||
|
||||
use anyhow::Context;
|
||||
use rule::{Confidence, RuleSyntax};
|
||||
use rule::{Confidence, RuleSyntax, Validation};
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
/// Custom error type for more granular rules loading errors.
|
||||
|
|
@ -28,6 +28,9 @@ pub enum RulesError {
|
|||
|
||||
#[error("Invalid ResponseMatcher variant in file: {0}, at line: {1}, column: {2}")]
|
||||
InvalidResponseMatcherVariant(String, usize, usize),
|
||||
|
||||
#[error("HTTP validation for rule `{rule_id}` in file {path} missing response_matcher")]
|
||||
MissingResponseMatcher { path: String, rule_id: String },
|
||||
}
|
||||
|
||||
/// Represents a collection of rule syntaxes.
|
||||
|
|
@ -58,6 +61,21 @@ impl Rules {
|
|||
match serde_yaml::from_reader::<_, Rules>(contents) {
|
||||
Ok(mut rs) => {
|
||||
rs.rules.retain(|rule| rule.confidence.is_at_least(&confidence));
|
||||
for rule_syntax in &rs.rules {
|
||||
if let Some(Validation::Http(http_val)) = &rule_syntax.validation {
|
||||
if http_val
|
||||
.request
|
||||
.response_matcher
|
||||
.as_ref()
|
||||
.map_or(true, |m| m.is_empty())
|
||||
{
|
||||
bail!(RulesError::MissingResponseMatcher {
|
||||
path: path.display().to_string(),
|
||||
rule_id: rule_syntax.id.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
rules.update(rs);
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,15 @@ fn default_true() -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
fn default_status_matcher() -> Vec<ResponseMatcher> {
|
||||
vec![ResponseMatcher::StatusMatch {
|
||||
r#type: "StatusMatch".to_string(),
|
||||
status: vec![200],
|
||||
match_all_status: false,
|
||||
negative: false,
|
||||
}]
|
||||
}
|
||||
|
||||
/// Represents various types of validation that a rule can perform.
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
|
||||
#[serde(tag = "type", content = "content")]
|
||||
|
|
@ -65,7 +74,7 @@ pub struct HttpRequest {
|
|||
#[serde(default)]
|
||||
pub headers: BTreeMap<String, String>,
|
||||
#[serde(default)]
|
||||
pub response_matcher: Vec<ResponseMatcher>,
|
||||
pub response_matcher: Option<Vec<ResponseMatcher>>,
|
||||
#[serde(default)]
|
||||
pub multipart: Option<MultipartConfig>,
|
||||
// allow HTML only when explicitly set true
|
||||
|
|
@ -73,6 +82,17 @@ pub struct HttpRequest {
|
|||
pub response_is_html: bool,
|
||||
}
|
||||
|
||||
|
||||
impl HttpRequest {
|
||||
/// Return the configured response matchers or a default StatusMatch 200.
|
||||
pub fn response_matchers_or_default(&self) -> Vec<ResponseMatcher> {
|
||||
self.response_matcher
|
||||
.clone()
|
||||
.unwrap_or_else(default_status_matcher)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Configuration for multipart HTTP requests.
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
|
||||
pub struct MultipartConfig {
|
||||
|
|
|
|||
|
|
@ -514,8 +514,11 @@ async fn timed_validate_single_match<'a>(
|
|||
|
||||
m.validation_response_status = status;
|
||||
m.validation_response_body = body.clone();
|
||||
let matchers = http_validation
|
||||
.request
|
||||
.response_matchers_or_default();
|
||||
m.validation_success = httpvalidation::validate_response(
|
||||
&http_validation.request.response_matcher,
|
||||
&matchers,
|
||||
&body,
|
||||
&status,
|
||||
&headers,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue