Files
zeekayandHanzo Dev 3e999cbb6d provider: a verdict is the provider's answer, never ours
LexisNexis reported "approved" out of InitiateVerification for any non-4xx reply
and again from CheckStatus with no request at all; Intellicheck issued its request
and then ignored the response, including its status code, so a 500 and a rejected
document both read as approved. None of the three read what the provider decided.
A caller gating on any of them would admit an unverified subject.

An integration that does not parse the response now refuses instead of answering,
and the refusal names the provider so an operator can tell it apart from a network
fault. LexisNexis loses the query builder nothing called; implementing it means
parsing the FlexID verdict and mapping it to a status.

Two properties are now table-driven over every constructible provider, so one added
later inherits both: a freshly created verification is never already approved, and
an unread status read refuses. The tests point at a server that ANSWERS — an
unreachable base URL would pass for the wrong reason, leaving the synthesized
approval downstream of a return no test reaches.

scripts/mutants.py drives cloud's strict-scored engine over both properties and the
webhook guards: 6 rows, 6 KILLED on a green baseline.

LLM.md now states per provider whether it can read a verdict and whether it can
prove a webhook, and carries the retirement runbook — sequenced on the route, since
an orphaned Ingress publishes idv.hanzo.ai today despite its CR disabling ingress.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-28 10:56:35 -07:00

66 lines
2.5 KiB
Go

// Package lexisnexis implements the IDV provider for LexisNexis FlexID.
package provider
import (
"context"
"fmt"
"net/http"
"time"
)
const (
LexisNexisProdURL = "https://wsonline.seisint.com/WsIdentity/FlexID"
LexisNexisSandboxURL = "https://wsonline-uat.seisint.com/WsIdentity/FlexID"
)
type LexisNexisConfig struct {
BaseURL string
Username string
Password string
}
type LexisNexis struct {
cfg LexisNexisConfig
client *http.Client
}
func NewLexisNexis(cfg LexisNexisConfig) *LexisNexis {
if cfg.BaseURL == "" {
cfg.BaseURL = LexisNexisSandboxURL
}
return &LexisNexis{cfg: cfg, client: &http.Client{Timeout: 30 * time.Second}}
}
func (p *LexisNexis) Name() string { return ProviderLexisNexis }
// InitiateVerification refuses.
//
// FlexID answers synchronously: the verdict — the comprehensive verification
// index and any watchlist hits — is IN the response body. This integration builds
// and sends the query correctly but has never parsed that body, so it cannot know
// what the provider decided. It previously returned "approved" for any non-4xx
// response, which reports a decision the provider never gave and would pass a
// subject FlexID had actually flagged.
//
// A provider that cannot read a verdict must not report one, so this refuses
// rather than inventing a pass. Implementing it means parsing the FlexID response
// — the comprehensive verification index and the watchlist section — and mapping
// that to a status; the query that produced it is in this file's history.
func (p *LexisNexis) InitiateVerification(ctx context.Context, user *VerificationRequest) (*VerificationResponse, error) {
return nil, fmt.Errorf("lexisnexis: the FlexID response carries the verdict and is not parsed — no decision can be reported")
}
// CheckStatus refuses, for the same reason InitiateVerification does: there is no
// stored or fetched verdict to report. It previously answered "approved"
// unconditionally, without so much as a request.
func (p *LexisNexis) CheckStatus(ctx context.Context, sessionID string) (*VerificationStatusResult, error) {
return nil, fmt.Errorf("lexisnexis: no verdict is parsed — status cannot be reported")
}
// ParseWebhook refuses every payload: FlexID answers in the response to the
// query, so nothing legitimate arrives here and there is no signature scheme a
// caller's claim could be checked against.
func (p *LexisNexis) ParseWebhook(body []byte, headers map[string]string) (*WebhookEvent, error) {
return nil, fmt.Errorf("lexisnexis answers synchronously and sends no webhook: %w", ErrWebhookUnsigned)
}