Documentation
¶
Overview ¶
Package spam scans caller-selected package text for promotional signals. It returns weighted findings, original byte offsets and per-file measurements. Scores are uncalibrated; callers supply any thresholds or enforcement policy.
ScanFile uses the built-in defaults. NewScanner accepts additional raw-text rules, weight overrides and disabled rule IDs. Reuse a Scanner across calls; each scan has independent mutable state and supports concurrent use.
Documents are UTF-8 bytes supplied by the caller. Paths are labels: scanning does not open files, fetch URLs, render markup or execute package contents. Input limits apply to the full call, including byte-identical duplicates. Evidence excerpts contain untrusted text and need escaping when displayed.
Index ¶
Examples ¶
Constants ¶
const ( // MaxDocumentBytes limits the size of each UTF-8 document. MaxDocumentBytes = 512 << 10 // MaxPackageBytes limits the total input bytes, including duplicates. MaxPackageBytes = 2 << 20 // MaxDocuments limits the number of documents, including duplicates. MaxDocuments = 64 )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Coverage ¶
type Coverage struct {
Path string `json:"path"`
SHA256 string `json:"sha256"`
Bytes int `json:"bytes"`
Status string `json:"status"`
DuplicateOf string `json:"duplicate_of,omitempty"`
}
Coverage identifies supplied text and whether it was scanned or deduplicated.
type Evidence ¶
type Evidence struct {
Path string `json:"path"`
Start int `json:"start"`
End int `json:"end"`
Excerpt string `json:"excerpt"`
Count int `json:"count"`
Detail string `json:"detail"`
}
Evidence identifies a match using original byte offsets [Start, End). Excerpt is a bounded copy of untrusted input text, not safe HTML.
type Match ¶
type Match struct {
Rule string `json:"rule"`
Weight int `json:"weight"`
Contribution int `json:"contribution"`
Evidence []Evidence `json:"evidence"`
}
Match records a rule's evidence, configured weight and grouped contribution.
type Measurement ¶
type Measurement struct {
Path string `json:"path"`
Rule string `json:"rule"`
Count int `json:"count"`
TextBytes int `json:"text_bytes"`
URLBytes int `json:"url_bytes,omitempty"`
TextCount int `json:"text_count,omitempty"`
OfferCount int `json:"offer_count,omitempty"`
ContextCount int `json:"context_count,omitempty"`
}
Measurement records per-document counts, including below-threshold findings. Custom expression counts are boolean: zero or one per document.
type Options ¶
type Options struct {
// Rules appends custom expressions after the built-in rules, in this order.
// Definitions are trusted configuration; compilation has no resource budget.
Rules []Rule
// A zero weight removes the score contribution, retaining evidence.
Weights map[string]int
// DisabledRules removes rules from matches, measurements and weights.
// Disabled custom expressions are not compiled or syntax-checked.
// A disabled rule cannot also appear in Weights.
DisabledRules []string
}
Options configures a scanner. NewScanner copies the supplied definitions and weights.
type Result ¶
type Result struct {
Ruleset string `json:"ruleset"`
Configuration string `json:"configuration"`
Weights map[string]int `json:"weights"`
Score int `json:"score"`
Matches []Match `json:"matches"`
Measurements []Measurement `json:"measurements"`
Coverage []Coverage `json:"coverage"`
Documents int `json:"documents"`
DuplicateDocuments int `json:"duplicate_documents"`
}
Result contains evidence and measurements, without a verdict or threshold. Configuration is a SHA-256 identity of definitions, rule order and weights.
func ScanFile ¶
ScanFile scans one file with default weights using a shared scanner.
Example ¶
package main
import (
"fmt"
"log"
"github.com/git-pkgs/spam"
)
func main() {
result, err := spam.ScanFile("package.json", []byte(`{"description":"Click here. Buy now. Download now."}`))
if err != nil {
log.Fatal(err)
}
for _, match := range result.Matches {
fmt.Println(match.Rule, match.Contribution, match.Evidence[0].Path)
}
}
Output: PROMOTIONAL_PHRASES 1 package.json
type Rule ¶
type Rule struct {
ID string `json:"id"`
Description string `json:"description"`
Category string `json:"category"`
Expression string `json:"expression,omitempty"`
DefaultWeight int `json:"default_weight"`
Provenance string `json:"provenance"`
}
Rule describes a built-in signal or a caller-supplied raw-text expression. Expression is empty for built-in structural rules. Custom expressions use git-pkgs/scan syntax with UTF-8 matching; inline flags such as (?i) are supported. Provenance records the author or upstream source and any adaptations.
type Scanner ¶
type Scanner struct {
// contains filtered or unexported fields
}
Scanner holds compiled rules and supports concurrent scans. Construct it with NewScanner; its zero value is not usable.
func NewScanner ¶
NewScanner compiles enabled expressions once. IDs must be unique, weights nonnegative with their enabled sum fitting in an int, and enabled custom expressions must not match empty text.
Example ¶
package main
import (
"fmt"
"log"
"github.com/git-pkgs/spam"
)
func main() {
scanner, err := spam.NewScanner(spam.Options{
Rules: []spam.Rule{{
ID: "LOCAL_OFFER", Description: "Exclusive offer phrase", Category: "promotion",
Expression: `(?i)\bexclusive offer\b`, DefaultWeight: 1, Provenance: "local policy",
}},
Weights: map[string]int{"LOCAL_OFFER": 2},
})
if err != nil {
log.Fatal(err)
}
result, err := scanner.ScanFile("README.md", []byte("Exclusive offer. Exclusive offer."))
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Score, result.Matches[0].Evidence[0].Count)
}
Output: 2 1
func (*Scanner) Rules ¶
Rules returns an independent copy of the enabled definitions in match order. DefaultWeight is the definition's weight before Options.Weights overrides.
Example ¶
package main
import (
"fmt"
"log"
"github.com/git-pkgs/spam"
)
func main() {
scanner, err := spam.NewScanner(spam.Options{
DisabledRules: []string{"KEYWORD_REPETITION", "REPEATED_URLS", "LINK_HEAVY", "REPEATED_LINES"},
Weights: map[string]int{"PROMOTIONAL_PHRASES": 2},
})
if err != nil {
log.Fatal(err)
}
for _, rule := range scanner.Rules() {
fmt.Println(rule.ID, rule.Category, rule.DefaultWeight)
}
}
Output: PROMOTIONAL_PHRASES promotion 1
func (*Scanner) Scan ¶
Scan reads input bytes for the duration of the call without modifying or retaining them. Callers must not modify the input during a scan. Returned evidence owns its text.
Example ¶
package main
import (
"fmt"
"log"
"github.com/git-pkgs/spam"
)
func main() {
scanner, err := spam.NewScanner(spam.Options{
Weights: map[string]int{"PROMOTIONAL_PHRASES": 2},
})
if err != nil {
log.Fatal(err)
}
result, err := scanner.Scan([]spam.Document{
{Path: "package.json", Data: []byte(`{"description":"An example package"}`)},
{Path: "README.md", Data: []byte("Click here. Buy now. Download now.")},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Documents, result.Score)
}
Output: 2 2