Merge pull request #37 from Exagone313/certificate-expiration

Add certificate expiration support
This commit is contained in:
Chris C 2020-11-15 13:50:49 -05:00 committed by GitHub
commit d1f24dbea4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 55 additions and 10 deletions

View File

@ -161,13 +161,14 @@ Here are some examples of conditions you can use:
#### Placeholders
| Placeholder | Description | Example of resolved value |
|:----------------------- |:------------------------------------------------------- |:------------------------- |
| `[STATUS]` | Resolves into the HTTP status of the request | 404
| `[RESPONSE_TIME]` | Resolves into the response time the request took, in ms | 10
| `[IP]` | Resolves into the IP of the target host | 192.168.0.232
| `[BODY]` | Resolves into the response body. Supports JSONPath. | `{"name":"john.doe"}`
| `[CONNECTED]` | Resolves into whether a connection could be established | `true`
| Placeholder | Description | Example of resolved value |
|:-------------------------- |:--------------------------------------------------------------- |:------------------------- |
| `[STATUS]` | Resolves into the HTTP status of the request | 404
| `[RESPONSE_TIME]` | Resolves into the response time the request took, in ms | 10
| `[IP]` | Resolves into the IP of the target host | 192.168.0.232
| `[BODY]` | Resolves into the response body. Supports JSONPath. | `{"name":"john.doe"}`
| `[CONNECTED]` | Resolves into whether a connection could be established | `true`
| `[CERTIFICATE_EXPIRATION]` | Resolves into the duration before certificate expiration, in ms | 4461677039, 0 (if not using HTTPS)
#### Functions

View File

@ -35,6 +35,11 @@ const (
// Values that could replace the placeholder: true, false
ConnectedPlaceHolder = "[CONNECTED]"
// CertificateExpirationPlaceholder is a placeholder for the duration before certificate expiration, in milliseconds.
//
// Values that could replace the placeholder: 4461677039 (~52 days)
CertificateExpirationPlaceholder = "[CERTIFICATE_EXPIRATION]"
// LengthFunctionPrefix is the prefix for the length function
LengthFunctionPrefix = "len("
@ -142,6 +147,8 @@ func sanitizeAndResolve(list []string, result *Result) []string {
element = body
case ConnectedPlaceHolder:
element = strconv.FormatBool(result.Connected)
case CertificateExpirationPlaceholder:
element = strconv.FormatInt(int64(result.CertificateExpiration.Milliseconds()), 10)
default:
// if contains the BodyPlaceHolder, then evaluate json path
if strings.Contains(element, BodyPlaceHolder) {
@ -174,11 +181,11 @@ func sanitizeAndResolve(list []string, result *Result) []string {
return sanitizedList
}
func sanitizeAndResolveNumerical(list []string, result *Result) []int {
var sanitizedNumbers []int
func sanitizeAndResolveNumerical(list []string, result *Result) []int64 {
var sanitizedNumbers []int64
sanitizedList := sanitizeAndResolve(list, result)
for _, element := range sanitizedList {
if number, err := strconv.Atoi(element); err != nil {
if number, err := strconv.ParseInt(element, 10, 64); err != nil {
// Default to 0 if the string couldn't be converted to an integer
sanitizedNumbers = append(sanitizedNumbers, 0)
} else {

View File

@ -1,6 +1,7 @@
package core
import (
"strconv"
"testing"
"time"
)
@ -309,3 +310,32 @@ func TestCondition_evaluateWithConnectedFailure(t *testing.T) {
t.Errorf("Condition '%s' should have been a failure", condition)
}
}
func TestCondition_evaluateWithUnsetCertificateExpiration(t *testing.T) {
condition := Condition("[CERTIFICATE_EXPIRATION] == 0")
result := &Result{}
condition.evaluate(result)
if !result.ConditionResults[0].Success {
t.Errorf("Condition '%s' should have been a success", condition)
}
}
func TestCondition_evaluateWithCertificateExpirationGreaterThan(t *testing.T) {
acceptable := (time.Hour * 24 * 28).Milliseconds()
condition := Condition("[CERTIFICATE_EXPIRATION] > " + strconv.FormatInt(acceptable, 10))
result := &Result{CertificateExpiration: time.Hour * 24 * 60}
condition.evaluate(result)
if !result.ConditionResults[0].Success {
t.Errorf("Condition '%s' should have been a success", condition)
}
}
func TestCondition_evaluateWithCertificateExpirationGreaterThanFailure(t *testing.T) {
acceptable := (time.Hour * 24 * 28).Milliseconds()
condition := Condition("[CERTIFICATE_EXPIRATION] > " + strconv.FormatInt(acceptable, 10))
result := &Result{CertificateExpiration: time.Hour * 24 * 14}
condition.evaluate(result)
if result.ConditionResults[0].Success {
t.Errorf("Condition '%s' should have been a failure", condition)
}
}

View File

@ -169,6 +169,10 @@ func (service *Service) call(result *Result) {
result.Errors = append(result.Errors, err.Error())
return
}
if response.TLS != nil {
certificate := response.TLS.PeerCertificates[0]
result.CertificateExpiration = certificate.NotAfter.Sub(time.Now())
}
result.HTTPStatus = response.StatusCode
result.Connected = response.StatusCode > 0
result.Body, err = ioutil.ReadAll(response.Body)

View File

@ -45,6 +45,9 @@ type Result struct {
// Timestamp when the request was sent
Timestamp time.Time `json:"timestamp"`
// CertificateExpiration is the duration before the certificate expires
CertificateExpiration time.Duration `json:"certificate-expiration,omitempty"`
}
// ConditionResult result of a Condition