Save malformed entries, and put paths in environment variables

To allow scripts to access them.
This commit is contained in:
Andrew Ayer 2023-02-18 21:09:04 -05:00
parent ee8ae0c1f3
commit bd2bab5fcb
4 changed files with 49 additions and 10 deletions

View File

@ -138,6 +138,14 @@ The following environment variables are set for `malformed_cert` events:
: A human-readable string describing why the certificate is malformed. : A human-readable string describing why the certificate is malformed.
`ENTRY_FILENAME`
: Path to a file containing the JSON log entry. The file contains a JSON object with two fields, `leaf_input` and `extra_data`, as described in RFC 6962 Section 4.6.
`TEXT_FILENAME`
: Path to a file containing a text message describing the malformed certificate. This file contains the same text that certspotter uses in emails.
# JSON FILE FORMAT # JSON FILE FORMAT
Unless `-no_save` is used, certspotter saves a JSON file for every discovered certificate Unless `-no_save` is used, certspotter saves a JSON file for every discovered certificate

View File

@ -15,8 +15,30 @@ import (
) )
type malformedLogEntry struct { type malformedLogEntry struct {
Entry *logEntry Entry *logEntry
Error string Error string
EntryPath string
TextPath string
}
func (malformed *malformedLogEntry) entryJSON() any {
return struct {
LeafInput []byte `json:"leaf_input"`
ExtraData []byte `json:"extra_data"`
}{
LeafInput: malformed.Entry.LeafInput,
ExtraData: malformed.Entry.ExtraData,
}
}
func (malformed *malformedLogEntry) save() error {
if err := writeJSONFile(malformed.EntryPath, malformed.entryJSON(), 0666); err != nil {
return err
}
if err := writeTextFile(malformed.TextPath, malformed.Text(), 0666); err != nil {
return err
}
return nil
} }
func (malformed *malformedLogEntry) Environ() []string { func (malformed *malformedLogEntry) Environ() []string {
@ -27,6 +49,8 @@ func (malformed *malformedLogEntry) Environ() []string {
"ENTRY_INDEX=" + fmt.Sprint(malformed.Entry.Index), "ENTRY_INDEX=" + fmt.Sprint(malformed.Entry.Index),
"LEAF_HASH=" + malformed.Entry.LeafHash.Base64String(), "LEAF_HASH=" + malformed.Entry.LeafHash.Base64String(),
"PARSE_ERROR=" + malformed.Error, "PARSE_ERROR=" + malformed.Error,
"ENTRY_FILENAME=" + malformed.EntryPath,
"TEXT_FILENAME=" + malformed.TextPath,
"CERT_PARSEABLE=no", // backwards compat with pre-0.15.0; not documented "CERT_PARSEABLE=no", // backwards compat with pre-0.15.0; not documented
} }
} }

View File

@ -74,11 +74,12 @@ func monitorLog(ctx context.Context, config *Config, ctlog *loglist.Log, logClie
defer cancel() defer cancel()
var ( var (
stateDirPath = filepath.Join(config.StateDir, "logs", ctlog.LogID.Base64URLString()) stateDirPath = filepath.Join(config.StateDir, "logs", ctlog.LogID.Base64URLString())
stateFilePath = filepath.Join(stateDirPath, "state.json") stateFilePath = filepath.Join(stateDirPath, "state.json")
sthsDirPath = filepath.Join(stateDirPath, "unverified_sths") sthsDirPath = filepath.Join(stateDirPath, "unverified_sths")
malformedDirPath = filepath.Join(stateDirPath, "malformed_entries")
) )
for _, dirPath := range []string{stateDirPath, sthsDirPath} { for _, dirPath := range []string{stateDirPath, sthsDirPath, malformedDirPath} {
if err := os.Mkdir(dirPath, 0777); err != nil && !errors.Is(err, fs.ErrExist) { if err := os.Mkdir(dirPath, 0777); err != nil && !errors.Is(err, fs.ErrExist) {
return fmt.Errorf("error creating state directory: %w", err) return fmt.Errorf("error creating state directory: %w", err)
} }

View File

@ -157,12 +157,18 @@ func processCertificate(ctx context.Context, config *Config, entry *logEntry, ce
} }
func processMalformedLogEntry(ctx context.Context, config *Config, entry *logEntry, parseError error) error { func processMalformedLogEntry(ctx context.Context, config *Config, entry *logEntry, parseError error) error {
// TODO-4: save the malformed entry (in get-entries format) in the state directory so user can inspect it dirPath := filepath.Join(config.StateDir, "logs", entry.Log.LogID.Base64URLString(), "malformed_entries")
malformed := &malformedLogEntry{ malformed := &malformedLogEntry{
Entry: entry, Entry: entry,
Error: parseError.Error(), Error: parseError.Error(),
EntryPath: filepath.Join(dirPath, fmt.Sprintf("%d.json", entry.Index)),
TextPath: filepath.Join(dirPath, fmt.Sprintf("%d.txt", entry.Index)),
} }
if err := malformed.save(); err != nil {
return fmt.Errorf("error saving malformed log entry %d in %s (%q): %w", entry.Index, entry.Log.URL, parseError, err)
}
if err := notify(ctx, config, malformed); err != nil { if err := notify(ctx, config, malformed); err != nil {
return fmt.Errorf("error notifying about malformed log entry %d in %s (%q): %w", entry.Index, entry.Log.URL, parseError, err) return fmt.Errorf("error notifying about malformed log entry %d in %s (%q): %w", entry.Index, entry.Log.URL, parseError, err)
} }