Compare commits
No commits in common. "d57b9c1d2ad6ee9df915d7d53bfbb85cfc8df2ae" and "3f02da9041061789b5ec4ec48d883d3da32390a7" have entirely different histories.
d57b9c1d2a
...
3f02da9041
1 changed files with 10 additions and 61 deletions
71
README.md
71
README.md
|
|
@ -20,12 +20,11 @@ Use this as a template when you create your own checker.
|
||||||
- [Step 6: Wire It Up (main.go)](#step-6-wire-it-up-maingo)
|
- [Step 6: Wire It Up (main.go)](#step-6-wire-it-up-maingo)
|
||||||
- [Step 7: Create the Plugin Entrypoint](#step-7-create-the-plugin-entrypoint)
|
- [Step 7: Create the Plugin Entrypoint](#step-7-create-the-plugin-entrypoint)
|
||||||
5. [Optional: Standalone Human UI (`CheckerInteractive`)](#optional-standalone-human-ui-checkerinteractive)
|
5. [Optional: Standalone Human UI (`CheckerInteractive`)](#optional-standalone-human-ui-checkerinteractive)
|
||||||
6. [Optional: Declaring rule prerequisites (`RulePrecheck`)](#optional-declaring-rule-prerequisites-ruleprecheck)
|
6. [Running the Checker](#running-the-checker)
|
||||||
7. [Running the Checker](#running-the-checker)
|
7. [Testing with curl](#testing-with-curl)
|
||||||
8. [Testing with curl](#testing-with-curl)
|
8. [Deploying to happyDomain](#deploying-to-happydomain)
|
||||||
9. [Deploying to happyDomain](#deploying-to-happydomain)
|
9. [License & happyDomain compatibility](#license--happydomain-compatibility)
|
||||||
10. [License & happyDomain compatibility](#license--happydomain-compatibility)
|
10. [Going Further](#going-further)
|
||||||
11. [Going Further](#going-further)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -134,6 +133,7 @@ const ObservationKeyDummy = "dummy"
|
||||||
type DummyData struct {
|
type DummyData struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Score float64 `json:"score"`
|
Score float64 `json:"score"`
|
||||||
|
CollectedAt time.Time `json:"collected_at"`
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -197,6 +197,7 @@ func (p *dummyProvider) Collect(ctx context.Context, opts CheckerOptions) (any,
|
||||||
return &DummyData{
|
return &DummyData{
|
||||||
Message: message,
|
Message: message,
|
||||||
Score: score,
|
Score: score,
|
||||||
|
CollectedAt: time.Now(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -471,59 +472,6 @@ Returning an error from `ParseForm` re-renders the form with the error message d
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Optional: Declaring rule prerequisites (`RulePrecheck`)
|
|
||||||
|
|
||||||
Some rules cannot run unless the operator has supplied a prerequisite, typically an API key or auth token in an admin option. Without a way to advertise that, the UI shows the rule as if it were ready and the user only discovers the problem after a check returns an error. The SDK exposes an optional per-rule hook so the host can tell ahead of time which rules are usable with the current options.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type RulePrecheck interface {
|
|
||||||
CheckRule
|
|
||||||
Precheck(ctx context.Context, opts CheckerOptions) error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Return `nil` when the rule is ready to run; return an error describing the missing prerequisite otherwise. The error message is shown verbatim to the operator, so name the option they need to set (`"VirusTotal API key is not configured"` is much more useful than `"missing key"`).
|
|
||||||
|
|
||||||
### Minimal implementation
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (r *vtRule) Precheck(ctx context.Context, opts sdk.CheckerOptions) error {
|
|
||||||
if v, _ := opts["virustotal_api_key"].(string); v == "" {
|
|
||||||
return fmt.Errorf("VirusTotal API key is not configured")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### How the host consumes it
|
|
||||||
|
|
||||||
`GET /definition` is unchanged: it returns the static checker definition with no per-rule availability information. Whenever the host has a concrete set of options at hand (rendering the rule list with the user's admin/user settings, validating an edit, …) it issues a `POST /definition` carrying those options:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:8080/definition \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"options": {}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"precheck_failures": {
|
|
||||||
"virustotal": "VirusTotal API key is not configured"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Rules that pass precheck, or that do not implement `RulePrecheck`, are absent from the map. The UI uses this to grey out unconfigured rules and surface the message returned by `Precheck`.
|
|
||||||
|
|
||||||
### Important: Precheck is advisory only
|
|
||||||
|
|
||||||
The SDK never calls `Precheck` from `Collect` or `Evaluate`. If the user enables a rule whose precheck fails and the host runs `/collect` anyway, your `Collect` (or the rule itself) still has to short-circuit and surface its own error: precheck is purely a UI hint, not an admission gate.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
## Running the Checker
|
## Running the Checker
|
||||||
|
|
||||||
### Build and run locally
|
### Build and run locally
|
||||||
|
|
@ -577,6 +525,7 @@ Response:
|
||||||
"data": {
|
"data": {
|
||||||
"message": "Testing my checker!",
|
"message": "Testing my checker!",
|
||||||
"score": 73.2,
|
"score": 73.2,
|
||||||
|
"collected_at": "2026-01-15T10:30:00Z"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -588,7 +537,7 @@ curl -X POST http://localhost:8080/evaluate \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"observations": {
|
"observations": {
|
||||||
"dummy": "{\"message\":\"test\",\"score\":42.5}"
|
"dummy": "{\"message\":\"test\",\"score\":42.5,\"collected_at\":\"2026-01-15T10:30:00Z\"}"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"warningThreshold": 50,
|
"warningThreshold": 50,
|
||||||
|
|
@ -621,7 +570,7 @@ Status codes: `1` = OK, `3` = Warning, `4` = Critical.
|
||||||
curl -X POST http://localhost:8080/report \
|
curl -X POST http://localhost:8080/report \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"data": "{\"message\":\"test\",\"score\":73.2}"
|
"data": "{\"message\":\"test\",\"score\":73.2,\"collected_at\":\"2026-01-15T10:30:00Z\"}"
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue