| service | securityhub | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sdk_module | aws-sdk-go-v2/service/securityhub@v1.75.4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| last_audit_commit | 1659d616 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| last_audit_date | 2026-07-25 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| overall | A | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ops |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| families |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gaps |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deferred | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| leaks |
|
The Go SDK module was bumped, revealing 7 operations added to
aws-sdk-go-v2/service/securityhub since the previous audit: CreateConnector,
GetConnector, UpdateConnector, DeleteConnector, ListConnectors (a new
CSPM third-party cloud-provider connector family -- see the "Traps" note
above for why this is not the same as the existing ConnectorV2 family),
and EnableSecurityHubFeatureV2/DisableSecurityHubFeatureV2 (opt-in feature
toggles scoped to the existing SecurityHub V2 hub state). All 7 were
implemented for real (routing, backend state, request parsing, response wire
shapes field-diffed against the SDK's own types/serializers.go/
deserializers.go, error codes, HTTP status, Snapshot/Restore persistence)
and added to GetSupportedOperations() -- none went into the
TestSDKCompleteness notImplemented list (which stayed empty).
Key design decisions:
EnableSecurityHubFeatureV2/DisableSecurityHubFeatureV2are wired to the existingHubV2state, not an orphan boolean. The real API's/hubv2/feature/{FeatureName}path and its documented "the service must be enabled before you can enable a feature" precondition both point at the existing V2 hub. Features are stored asHubV2.Features map[string]*HubV2Feature(new field on the existing struct) rather than a separate backend field, so they persist/reset with the V2 hub's own lifecycle for free (no new Snapshot/Restore wiring needed) andDescribeSecurityHubV2-- the existing op -- now reports them, matching the realDescribeSecurityHubV2Output.Featuresfield that also arrived in this SDK bump.- CSPM Connectors' authorization lifecycle is modeled honestly, not
auto-completed. Unlike Connectors V2 (which has
RegisterConnectorV2to complete an out-of-band OAuth handshake), the real CSPM Connector surface has no such operation at all -- see thegapsentry above. A connector created viaCreateConnectoris left atEnablementStatus=PENDING_ENABLEMENT/ healthConnectorStatus=UNKNOWNpermanently, since no real client action this backend can observe would legitimately advance it further. - Bonus fix, found while wiring
FeaturesintoDescribeSecurityHubV2: its response previously returned inventedCreatedAt/UpdatedAtfields; the realDescribeSecurityHubV2Output(confirmed in both v1.71.2 and v1.75.0, so this predates the SDK bump) is{Features, HubV2Arn, SubscribedAt}. Fixed in the same handler function this pass touched anyway to addFeatures.
Fresh audit (this service had no PARITY.md before the 2026-07-23 pass). Persistence (Handler.Snapshot/Restore delegating to InMemoryBackend) was added recently and verified intact -- no changes needed there.
-
handler_configpolicy.go-- ConfigurationPolicyAssociationTargetTypealways empty.GetConfigurationPolicyAssociation,StartConfigurationPolicyAssociation, andStartConfigurationPolicyDisassociationall read a"TargetType"key out of the request'sTargetobject. The real wire shape (types.Targetis a Smithy tagged union -- seeserializers.go:34632 awsRestjson1_serializeDocumentTarget) never sends that field; the request is one of{"AccountId":...}/{"OrganizationalUnitId":...}/{"RootId":...}andTargetType(ACCOUNT/ORGANIZATIONAL_UNIT/ROOT) must be derived from which key is present. Every association response'sTargetTypefield was silently empty for every real SDK client. Fixed by addingextractConfigPolicyTarget(derives ID + type from the union) and using it at all three call sites. Covered byTestParity_ConfigurationPolicyAssociation_TargetTypeDerived(parity_d_test.go). -
backend_members.go--InviteMembersnever validated the account exists. AWS requiresCreateMembersbeforeInviteMembers; inviting an account that was never created must land inUnprocessedAccounts. The previous implementation unconditionally created anInvitationfor every account ID with no existence check, soUnprocessedAccountswas always empty regardless of input validity -- a disguised no-op on the validation path. Fixed to checkb.members.Get(id)first and populateUnprocessedAccounts(ResourceNotFoundException) for unknown accounts, matching the same pattern already used byDeleteMembers/GetMembers. Covered byTestParity_InviteMembers_UnknownAccountUnprocessed. -
backend_v2.go--UpdateAutomationRuleV2silently droppedActionsupdates. The handler passes the raw decoded JSON request body straight through asupdates map[string]any. A JSON array decodes into[]any(each elementmap[string]any), but the backend assertedupdates["Actions"].([]map[string]any)directly -- an assertion that can never succeed against[]any, so everyActionsupdate was silently dropped while every other field updated fine. Fixed to convert[]any->[]map[string]anyelement-by-element, mirroring the pattern already used correctly inBatchUpdateAutomationRules(V1) and the V2 create handler. Covered byTestParity_UpdateAutomationRuleV2_ActionsApplied.
-
findings.go--GetFindings/GetFindingsV2acceptedSortCriteriabut silently discarded it (results returned in map-iteration order, effectively random). AddedsortFindings(stable multi-key sort overtypes.SortCriterion'sField/SortOrder"asc"/"desc" wire shape), wired into bothGetFindingsand the newGetFindingsV2. Covered byTestGetFindings_SortCriteria(findings_test.go). -
findings.go--BatchImportFindingsre-import overwroteNote/UserDefinedFields/VerificationState/Workflowinstead of preserving them. AWS documents ("After a finding is created,BatchImportFindingscannot be used to update the following finding fields...") that these four fields are retained from the finding's previous version regardless of what a re-import request supplies.ImportFindingspreviously did a flatmaps.Copythat let any subsequent import silently reset a customer's investigation Note/Workflow/etc. Fixed withpreserveCustomerManagedFields, which restores (or deletes, if never set) these fields from the prior stored version after every re-import. Covered byTestBatchImportFindings_PreservesCustomerManagedFields. -
findings.go--GetFindingHistorywas a hardcoded stub returning{Records: []}always; no finding-update history was ever recorded. Added afindingHistory map[string][]map[string]anystore field (snapshot-persisted alongsidefindings, same plain-map pattern) andrecordFindingHistory/diffFindingFieldshelpers.ImportFindingsnow records aFindingCreated: trueentry for new findings and a field-diff entry for re-imports;BatchUpdateFindingsandUpdateFindingseach record a field-diff entry per mutated finding (excluding theCreatedAt/UpdatedAt/FirstObservedAt/LastObservedAttimestamp fields AWS documents as excluded from history).GetFindingHistorynow filters the recorded log byStartTime/EndTimeand paginates it (100 per page, matching AWS's documented cap). Covered byTestGetFindingHistory_RecordsChangesandTestGetFindingHistory_UnknownFinding. -
handler_findings.go--BatchUpdateFindingsV2read a nonexistent"FindingFieldsUpdate"wrapper key. The realBatchUpdateFindingsV2Inputwire shape (aws-sdk-go-v2/service/securityhub/api_op_BatchUpdateFindingsV2.go) is flat:Comment,FindingIdentifiers([]types.OcsfFindingIdentifier),MetadataUids,SeverityId,StatusId-- there is no wrapper object, so every real client request was silently a no-op. Additionally,FindingIdentifiersusesCloudAccountUid/FindingInfoUid/MetadataProductUid(types.OcsfFindingIdentifier), not V1'sProductArn/Id, so even after fixing the wrapper-key bug the old delegation to V1BatchUpdateFindingscould never match a stored finding. Rewrote as a dedicatedBatchUpdateFindingsV2backend method (findings_v2.go) that parses the flat request fields and resolvesCloudAccountUid/FindingInfoUid/MetadataProductUidagainst the stored finding'sAwsAccountId/Id/ProductArn-- the only viable mapping since this mock has no separate OCSF ingestion API (findings only ever enter via V1BatchImportFindings). Covered byTestBatchUpdateFindingsV2_WireShapeandTestBatchUpdateFindingsV2_UnmatchedIdentifiers(findings_v2_test.go). -
handler_findings.go--GetFindingsV2Filterswas passed straight to the V1matchesFindingFilters, which looks for top-levelId/ProductArn/etc. keys. The realGetFindingsV2Filterswire shape istypes.OcsfFindingFilters:{CompositeFilters: [...], CompositeOperator: "AND"|"OR"}, eachCompositeFilterholdingStringFilters/NumberFilters/etc. keyed by an OCSF field name (types.OcsfStringField/OcsfNumberField) plus its ownOperator. None of those keys exist in the V1 filter shape, so every real V2 client'sFilterswas a complete no-op (matched everything) rather than merely "unsorted" -- worse than the PARITY.md entry previously on file suggested. AddedmatchesFindingFiltersV2+matchesCompositeFilter/matchesOcsfStringFilter/matchesOcsfNumberFilter(findings_v2.go), which evaluate the real nested shape against a field-name-mapped subset of the stored ASFF finding (seeocsfStringFieldMap/ocsfNumberFieldMapand the residual-gap entry above).severity_id/status_idNumberFilters round-trip theSeverityId/StatusIdfieldsBatchUpdateFindingsV2itself writes (fix #7), giving V2 update + V2 filter a coherent, testable round trip. Covered byTestGetFindingsV2_CompositeFilters.
The previous pass (fix #8 above) evaluated only StringFilters/NumberFilters
within each CompositeFilter; DateFilters, MapFilters, IpFilters,
BooleanFilters, and NestedCompositeFilters were accepted on the wire and
silently ignored -- worse than an error, since a caller got HTTP 200 and an
unfiltered result set with no indication their filter did nothing. Field-diffed
the full real taxonomy (types.CompositeFilter, types.Ocsf*Filter,
types.Ocsf*Field enums, types.StringFilter/MapFilter/DateFilter/
IpFilter/BooleanFilter/NumberFilter/DateRange,
types.AllowedOperators/StringFilterComparison/MapFilterComparison/
DateRangeComparison/DateRangeUnit) against aws-sdk-go-v2/service/ securityhub@v1.75.0's types/types.go and types/enums.go directly (not
against this handler's own prior output).
Filter types implemented this pass, each restructured into its own small
result-collector (stringFilterResults/numberFilterResults/
dateFilterResults/mapFilterResults/ipFilterResults/
booleanFilterResults/nestedCompositeFilterResults) feeding a single
matchesCompositeFilterDepth combinator (decomposed to keep CodeFactor's
Complex Method check quiet -- no nolint):
- DateFilters (
ocsfDateFieldMap):finding_info.created_time_dt->CreatedAt,finding_info.first_seen_time_dt->FirstObservedAt,finding_info.last_seen_time_dt->LastObservedAt,finding_info.modified_time_dt->UpdatedAt-- all genuine ASFF finding-level timestamps. Both comparator shapes are implemented: absoluteStart/Endbounds (matchesDateStartEnd), and relativeDateRange{Comparison: WITHIN|OLDER_THAN, Unit: DAYS, Value}(matchesDateRange) --WITHINmatches at-or-afternow - Value days,OLDER_THANits strict complement.resources.image.*/resources.modified_time_dthave no ASFF equivalent (ASFF'sResourcecarries no image/per-resource-modified timestamp) and are unmapped. - MapFilters (
mapFilterCandidates):resources.tags-> per-resourceResources[].Tags,finding_info.tags-> the finding-levelUserDefinedFieldsmap (the closest real ASFF analog to a finding-level "tag"),compliance.control_parameters->Compliance. SecurityControlParameters[]{Name,Value[]}. All fourMapFilterComparisonvalues implemented (EQUALS/NOT_EQUALS/CONTAINS/NOT_CONTAINS) viacompareMapFilter, with positive comparisons OR'd and negative ones AND'd across multiple candidate values for the same key (mirrors the documented same-field combination rule).databucket.tagshas no ASFF concept at all and is unmapped. - IpFilters (
ipFieldNetworkKeys):evidences.src_endpoint.ip->Network.SourceIpV4/SourceIpV6,evidences.dst_endpoint.ip->Network.DestinationIpV4/DestinationIpV6-- ASFF has no "evidences" concept, butNetwork's source/destination IP fields are the only genuinely analogous data this store carries.IpFilterhas only aCidrfield (no comparator) -- CIDR containment vianet.ParseCIDR/IPNet.Contains, with a bare IP address normalized to an exact-match/32or/128per AWS's documented "CIDR block or single IP" input. - BooleanFilters: only
vulnerabilities.is_exploit_availableis evaluated --Vulnerability.ExploitAvailableis a genuine two-valued ASFF enum (YES/NO), so it round-trips to bool cleanly; a finding matches if ANY entry in itsVulnerabilitiesarray has a matching value.vulnerabilities.is_fix_availableis deliberately NOT evaluated:Vulnerability.FixAvailableis three-valued (YES/NO/PARTIAL), and collapsingPARTIALinto eithertrueorfalsewould silently misclassify findings -- worse than leaving it unfiltered.compliance.assessments.meets_criteriahas no ASFF backing at all (no "assessments" concept onCompliance) and is also unmapped. - NumberFilters bonus: added
confidence_score-> ASFF's own top-levelConfidence(int 0-100) toocsfNumberFieldMap-- a clean scalar match found while auditing the taxonomy, not part of the original gap list.
NestedCompositeFilters: recurses fully via matchesCompositeFilterDepth
-- each nested CompositeFilter is evaluated as its own sub-tree (including
its own further NestedCompositeFilters) and the resulting bool joins its
parent's result list, combined by the parent's own Operator. This was
chosen over half-evaluating (e.g. only reading direct filters and ignoring
nesting) because a partially-evaluated boolean tree returns wrong
results, not merely unfiltered ones -- see the task's own warning, confirmed
by a regression-style test case
(NestedCompositeFilters_AND_recurses_and_requires_both_branches): a single
finding can't have two different AwsAccountId values, so ANDing two
mutually-exclusive nested branches must match zero findings; before this
fix (NestedCompositeFilters unevaluated -> empty result list -> vacuous
match-all), that same request would have wrongly matched both seeded
findings. Recursion depth is capped at maxNestedCompositeDepth = 5 (AWS
documents the real structure as capped at 3 layers; 5 is a defensive margin
against a pathological/hand-crafted request, not a limit real traffic
should approach). Note types.AllowedOperators has only AND/OR -- there
is no logical NOT combinator in the real API; negation is expressed at the
leaf via NOT_* comparators (NOT_EQUALS/NOT_CONTAINS/
PREFIX_NOT_EQUALS), not a boolean-tree NOT node, so AND/OR recursion is
the complete real semantics.
Comparator verification: StringFilterComparison
(EQUALS/PREFIX/NOT_EQUALS/PREFIX_NOT_EQUALS/CONTAINS/
NOT_CONTAINS/CONTAINS_WORD) was already correctly implemented by
compareStringFilter (reused unchanged) -- confirmed against types.go's
enum values and StringFilter's doc comments describing each comparator's
exact semantics (including the CONTAINS_WORD-only-in-V2-APIs note).
MapFilterComparison (EQUALS/NOT_EQUALS/CONTAINS/NOT_CONTAINS, no
PREFIX variant -- confirmed the enum has no PREFIX member) implemented fresh
in compareMapFilter following the same positive-OR/negative-AND doc
pattern. DateRangeComparison (WITHIN/OLDER_THAN, default WITHIN per
doc) and the fact DateRangeUnit has only DAYS as of this SDK version
were both confirmed directly against enums.go. NumberFilter was
reconfirmed to have no Comparison field at all (Eq/Gt/Gte/Lt/Lte
only) -- unchanged from the prior pass.
Tests: extended TestGetFindingsV2_CompositeFilters (existing table) with
two confidence_score cases, and added a new table test
TestGetFindingsV2_CompositeFilters_DateMapIPBooleanNested covering every
implemented filter type with paired cases that each narrow to exactly one of
two seeded findings with deliberately divergent field values (proving actual
discrimination, not a "matches everything" false pass), plus the
AND/OR nested-recursion pair described above.
Extracted every op's HTTP method + URI template directly from
aws-sdk-go-v2/service/securityhub@v1.71.2/serializers.go
(awsRestjson1_serializeOpHttpBindings* / SplitURI calls) for all ~105
operations and cross-checked against classifyPath's per-family
classify*Path functions in handler.go, handler_members.go,
handler_configpolicy.go, and handler_v2.go. All method+path pairs match.
RouteMatcher (handler.go) was separately checked to confirm every prefix
classifyPath switches on is also covered by RouteMatcher's
unambiguous-prefix OR-chain, so no routed op is reachable by Handler()
directly (bypassing the matcher, as unit tests do) but unreachable through
the real Echo route registration. No route-matcher bugs found in this
service.
/automationrulesv2is astrings.HasPrefixsuperset of/automationrules(both share the/automationrulessubstring) --classifyPath's switch correctly orders the V2 case before the V1 case. Don't "simplify" that ordering.- (parity-4) Same trap, new pair:
/connectorsv2is astrings.HasPrefixsuperset of the new plain/connectors(CSPM connectors).pathClassifiersinhandler.goordershasPathPrefix(pathConnectorsV2)beforehasPathPrefix(pathConnectors)-- don't reorder or collapse them. Also note:CreateConnector/GetConnector/etc. (this pass) andCreateConnectorV2/GetConnectorV2/etc. are two entirely unrelated real AWS features that happen to share the word "connector" -- CSPM connectors link to third-party cloud providers (Azure), Connectors V2 link to third-party ticketing systems (Jira/ServiceNow). Modeled as distinct Go types (CspmConnectorvsConnectorV2) and distinct backend/handler files (connectors.go/handler_connectors.govsconnectors_v2.go/handler_connectors_v2.go) specifically to avoid conflating them. classifyConfigPolicyPath's PATCH/DELETE cases match/configurationPolicy/with explicit exclusions forcreate/get/listsuffixes rather than a positive{Identifier}pattern -- this is intentional (mirrors the real flat-path-segment routing) and correct as long as no realConfigurationPolicyIdentifiervalue is literally"create","get", or"list".BatchUpdateFindings/ImportFindings/GetFindingsdo not checkhubEnabled(unlikeUpdateFindings/insights/action-targets). This was investigated and left as-is: AWS's own docs don't clearly state these ops require the hub to be enabled, and no existing test asserts either behavior, so flipping it risks breaking passing integrations without clear spec backing. Revisit if a concrete AWS error transcript surfaces.