Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions appsec/appsec.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const (
// This function always returns nil when appsec is disabled.
func MonitorParsedHTTPBody(ctx context.Context, body any) error {
if !appsec.Enabled() {
appsecDisabledLog.Do(func() { log.Warn("appsec: not enabled. Body blocking checks won't be performed.") })
appsecDisabledLog.Do(func() { log.Warn("appsec: not enabled. Body blocking checks won't be performed.") }) //errtrack:ignore caller used the AppSec API while disabled
return nil
}
return httpsec.MonitorParsedBody(ctx, body)
Expand All @@ -58,7 +58,7 @@ func MonitorParsedHTTPBody(ctx context.Context, body any) error {
// are ignored if AppSec is disabled or the provided context is incorrect.
func MonitorHTTPResponseBody(ctx context.Context, body any) error {
if !appsec.Enabled() {
appsecDisabledLog.Do(func() { log.Warn("appsec: not enabled. Body blocking checks won't be performed.") })
appsecDisabledLog.Do(func() { log.Warn("appsec: not enabled. Body blocking checks won't be performed.") }) //errtrack:ignore caller used the AppSec API while disabled
return nil
}
return httpsec.MonitorResponseBody(ctx, body)
Expand Down Expand Up @@ -89,7 +89,7 @@ func setUser(ctx context.Context, id string, userEventType usersec.UserEventType
s.Root().SetTag("_dd.appsec.user.collection_mode", collectionMode)

if !appsec.Enabled() {
appsecDisabledLog.Do(func() { log.Warn("appsec: not enabled. User blocking checks won't be performed.") })
appsecDisabledLog.Do(func() { log.Warn("appsec: not enabled. User blocking checks won't be performed.") }) //errtrack:ignore caller used the AppSec API while disabled
// Not returning here, as we still want to record the relevant span tags (just no WAF call).
}

Expand Down Expand Up @@ -197,7 +197,7 @@ func TrackCustomEvent(ctx context.Context, name string, md map[string]string) {
func getRootSpan(ctx context.Context) *tracer.Span {
span, _ := tracer.SpanFromContext(ctx)
if span == nil {
log.Warn("appsec: user event monitoring SDK: could not find a span in the provided context.Context")
log.Warn("appsec: user event monitoring SDK: could not find a span in the provided context.Context") //errtrack:ignore caller provided a context without a span
return nil
}
return span.Root()
Expand Down
2 changes: 1 addition & 1 deletion instrumentation/appsec/emitter/waf/actions/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func init() {
for key, template := range map[string]*[]byte{envBlockedTemplateJSON: &blockedTemplateJSON, envBlockedTemplateHTML: &blockedTemplateHTML} {
if path, ok := env.Lookup(key); ok {
if t, err := os.ReadFile(path); err != nil {
log.Error("Could not read template at %q: %v", path, err.Error())
log.Error("Could not read template at %q: %v", path, err.Error()) //errtrack:ignore user-provided template path
} else {
*template = t
}
Expand Down
34 changes: 34 additions & 0 deletions internal/appsec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,40 @@ All currently available features are the following ones:
| WAF Context | Setup of the request scoped context system of the WAF |
| Tracing | Bridge between the tracer and AppSec features |

### Error Tracking eligibility

The AppSec package marks each `log.Error` and `log.Warn` site with an
`//errtrack:ignore` directive when the logged failure is not eligible for Error
Tracking reporting. The directive carries the reason, so an audit does not have
to re-derive it from the code.

A site is ineligible only when its failure is outside the SDK's control or is
reported through another channel:

- The caller used the AppSec API while AppSec is disabled, or without the
request instrumentation that the API requires. These failures depend on the
application, not on the SDK.
- The failure comes from a user-provided value, such as a blocked-response
template path.
- The failure comes from the host or the native library, such as a
libddwaf compatibility issue on the host.
- The failure is an expected limit on the request path: the global trace rate
limit (`DD_APPSEC_TRACE_RATE_LIMIT`) or the maximum number of WAF events per
request.
- Remote Config reports the failure back to the backend through an apply
status. Only a site that always produces an apply status may use this reason.

A site without a directive stays actionable in the audit. Do not add a
directive to hide an unclassified site; remove or fix the site instead.

Startup timing is not a reason to leave a site unclassified. A report made
before `telemetry.StartApp` captures its stack trace eagerly at the call site
and is replayed once telemetry starts, so the stack still points at the real
call site. The failure mode to watch for is eviction from the 512-entry ring
buffer shared by every global telemetry call, not a bad stack trace. Prefer a
site that fires after `StartApp` when you have the choice; see the reporting
policy in [internal/README.md](../README.md) for the full criteria.

### AppSec state checks

`Enabled` and `RASPEnabled` load the active AppSec instance and its started state
Expand Down
2 changes: 1 addition & 1 deletion internal/appsec/appsec.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func (a *appsec) start() error {
return fmt.Errorf("error while loading libddwaf: %w", err)
}
// 2. If there is an error and the loading is ok: log as an informative error where appsec can be used
log.Error("appsec: non-critical error while loading libddwaf: %s", err.Error())
log.Error("appsec: non-critical error while loading libddwaf: %s", err.Error()) //errtrack:ignore host native-library compatibility failure
Comment thread
darccio marked this conversation as resolved.
}

// Register dyngo listeners
Expand Down
2 changes: 1 addition & 1 deletion internal/appsec/emitter/usersec/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const (
func StartUserLoginOperation(ctx context.Context, eventType UserEventType, args UserLoginOperationArgs) (*UserLoginOperation, *error) {
parent, ok := dyngo.FromContext(ctx)
if !ok { // Nothing will be reported in this case, but we can still block so we don't return
errorLogOnce.Do(func() { log.Error(errorLog) })
errorLogOnce.Do(func() { log.Error(errorLog) }) //errtrack:ignore request context lacks integration metadata
}

op := &UserLoginOperation{Operation: dyngo.NewOperation(parent), EventType: eventType}
Expand Down
4 changes: 2 additions & 2 deletions internal/appsec/emitter/waf/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func (op *ContextOperation) AddEvents(events ...any) bool {
}

if !op.limiter.Allow() {
log.Error("appsec: too many WAF events, stopping further reporting")
log.Error("appsec: too many WAF events, stopping further reporting") //errtrack:ignore expected global trace rate limiting on the request path
return true
}

Expand All @@ -172,7 +172,7 @@ func (op *ContextOperation) AddEvents(events ...any) bool {
const maxWAFEventsPerRequest = 10
if len(op.events) >= maxWAFEventsPerRequest {
op.logOnce.Do(func() {
log.Warn("appsec: ignoring new WAF event due to the maximum number of security events per request was reached")
log.Warn("appsec: ignoring new WAF event due to the maximum number of security events per request was reached") //errtrack:ignore expected per-request capacity limit
})
return true
}
Expand Down
6 changes: 3 additions & 3 deletions internal/appsec/emitter/waf/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (op *ContextOperation) runWAF(eventReceiver dyngo.Operation, runner libddwa
func RunSimple(ctx context.Context, addrs addresses.RunAddressData, errorLog string) error {
parent, _ := dyngo.FromContext(ctx)
if parent == nil {
log.Error("%s", errorLog)
log.Error("%s", errorLog) //errtrack:ignore request context lacks AppSec instrumentation
return nil
}

Expand All @@ -137,12 +137,12 @@ func RunSimple(ctx context.Context, addrs addresses.RunAddressData, errorLog str
func RunSimpleSubcontext(ctx context.Context, addrs addresses.RunAddressData, errorLog string) error {
parent, _ := dyngo.FromContext(ctx)
if parent == nil {
log.Error("%s", errorLog)
log.Error("%s", errorLog) //errtrack:ignore request context lacks AppSec instrumentation
return nil
}
ctxOp, ok := dyngo.FindOperation[ContextOperation](ctx)
if !ok {
log.Error("%s", errorLog)
log.Error("%s", errorLog) //errtrack:ignore request context lacks AppSec instrumentation
return nil
}
var err error
Expand Down
8 changes: 4 additions & 4 deletions internal/appsec/remoteconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (a *appsec) onRCRulesUpdate(updates map[string]remoteconfig.ProductUpdate)
}
cfg := UpdatedConfig{Product: product}
if err := json.Unmarshal(data, &cfg.Content); err != nil {
log.Error("appsec: unmarshaling remote config update for %s (%q): %s", product, path, err.Error())
log.Error("appsec: unmarshaling remote config update for %s (%q): %s", product, path, err.Error()) //errtrack:ignore failure is returned through Remote Config apply status
statuses[path] = state.ApplyStatus{State: state.ApplyStateError, Error: err.Error()}
continue
}
Expand Down Expand Up @@ -216,7 +216,7 @@ func (a *appsec) handleASMFeatures(u remoteconfig.ProductUpdate) map[string]stat
}

if len(u) > 1 {
log.Warn("appsec: Remote Config: received multiple ASM_FEATURES update; not processing any.")
log.Warn("appsec: Remote Config: received multiple ASM_FEATURES update; not processing any.") //errtrack:ignore failure is returned through Remote Config apply status
statuses := make(map[string]state.ApplyStatus, len(u))
for path := range u {
statuses[path] = state.ApplyStatus{State: state.ApplyStateUnacknowledged}
Expand Down Expand Up @@ -246,15 +246,15 @@ func (a *appsec) handleASMFeatures(u remoteconfig.ProductUpdate) map[string]stat
// Parse the config object we just received...
var parsed state.ASMFeaturesData
if err := json.Unmarshal(raw, &parsed); err != nil {
log.Error("appsec: remote config: error while unmarshalling %q: %s. Configuration won't be applied.", path, err.Error())
log.Error("appsec: remote config: error while unmarshalling %q: %s. Configuration won't be applied.", path, err.Error()) //errtrack:ignore failure is returned through Remote Config apply status
return map[string]state.ApplyStatus{path: {State: state.ApplyStateError, Error: err.Error()}}
}

// RC triggers activation of ASM; ASM is not started yet... Starting it!
if parsed.ASM.Enabled && !a.started.Load() {
log.Debug("appsec: remote config: Starting AppSec")
if err := a.start(); err != nil {
log.Error("appsec: remote config: error while processing %q. Configuration won't be applied: %s", path, err.Error())
log.Error("appsec: remote config: error while processing %q. Configuration won't be applied: %s", path, err.Error()) //errtrack:ignore failure is returned through Remote Config apply status
return map[string]state.ApplyStatus{path: {State: state.ApplyStateError, Error: err.Error()}}
}
registerAppsecStartTelemetry(config.RCStandby, telemetry.OriginRemoteConfig)
Expand Down
Loading