Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add signup sdk method #4865

Draft
wants to merge 50 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
81f6ab5
instrument passport
simon-id Sep 11, 2024
023828c
lint
simon-id Sep 11, 2024
98090fb
update config
simon-id Sep 11, 2024
5cc7274
add new RC capability
simon-id Sep 11, 2024
3343878
Merge branch 'master' into new_user_collection
simon-id Sep 11, 2024
4e127de
add RC handler
simon-id Sep 12, 2024
b4daf8f
fix require path
simon-id Sep 12, 2024
5f85b8e
add setCollectionMode()
simon-id Sep 12, 2024
1b074ad
use setCollectionMode() in appsec index
simon-id Sep 12, 2024
cbc8cf3
DRY up passport strategies instrumentation
simon-id Sep 17, 2024
729241d
simplify passport strategies instrumentations
simon-id Sep 17, 2024
6ffc21f
simplify instrumentation
simon-id Sep 17, 2024
4433baa
note for later
simon-id Sep 17, 2024
cf39858
add blocking to login
simon-id Sep 17, 2024
2da5bef
Merge branch 'master' into new_user_collection
simon-id Sep 17, 2024
d8bed47
add abortController in listener
simon-id Sep 17, 2024
5105006
cleanup
simon-id Oct 20, 2024
65c680b
update typings
simon-id Oct 20, 2024
23f965d
cleanup
simon-id Oct 20, 2024
b5b336a
RC config update
simon-id Oct 20, 2024
53a7791
push everything
simon-id Oct 30, 2024
2b7d2e6
simplify passport strategies instrumentation
simon-id Oct 30, 2024
24c2828
fixes
simon-id Oct 30, 2024
cdb89f6
simplify code
simon-id Oct 30, 2024
32edfae
cleanup
simon-id Oct 30, 2024
50b3605
revert some changes
simon-id Oct 30, 2024
b5aa0d4
delete passport.js
simon-id Oct 30, 2024
1f30a3c
Update packages/datadog-instrumentations/src/passport-utils.js
simon-id Oct 30, 2024
9fee2bc
update verify subscriber
simon-id Oct 30, 2024
be3115a
rollback config changes
simon-id Oct 30, 2024
38b4419
fix config
simon-id Oct 30, 2024
40c9df1
add blocking for passport strategies
simon-id Oct 30, 2024
e34bb72
update typings and docs
simon-id Oct 30, 2024
d255702
update appsec index
simon-id Oct 30, 2024
b6d4584
update RC
simon-id Oct 30, 2024
75782f9
Merge branch 'master' into new_user_collection
simon-id Oct 30, 2024
4ff8379
push some stuff
simon-id Oct 30, 2024
be473e6
push some stuff
simon-id Oct 30, 2024
4a566a8
cleanup
simon-id Oct 30, 2024
882bc8b
cleanup
simon-id Oct 30, 2024
d4345ef
add new usr.login waf address
simon-id Oct 31, 2024
f129890
cleanup
simon-id Oct 31, 2024
58897c8
Merge branch 'new_user_collection' into automatic_userid_blocking
simon-id Oct 31, 2024
cceac23
cleanup
simon-id Oct 31, 2024
654d260
add comment for later
simon-id Oct 31, 2024
066da87
add some ideas
simon-id Oct 31, 2024
da9b270
cleanup
simon-id Nov 1, 2024
01c8240
aaaaa
simon-id Nov 1, 2024
186d22d
Add signup sdk method
uurien Nov 6, 2024
ac0b21e
Add entry in test.ts
uurien Nov 6, 2024
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
3 changes: 3 additions & 0 deletions docs/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,9 @@ tracer.appsec.trackUserLoginFailureEvent('user_id', true, meta)
tracer.appsec.trackUserLoginFailureEvent('user_id', false)
tracer.appsec.trackUserLoginFailureEvent('user_id', false, meta)

tracer.appsec.trackUserSignupEvent('user_id')
tracer.appsec.trackUserSignupEvent('user_id', meta)

tracer.appsec.trackCustomEvent('event_name')
tracer.appsec.trackCustomEvent('event_name', meta)

Expand Down
47 changes: 34 additions & 13 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -654,12 +654,24 @@ declare namespace tracer {
*/
eventTracking?: {
/**
* Controls the automated user event tracking mode. Possible values are disabled, safe and extended.
* On safe mode, any detected Personally Identifiable Information (PII) about the user will be redacted from the event.
* On extended mode, no redaction will take place.
* @default 'safe'
* Controls the automated user tracking mode for user IDs and logins collections. Possible values:
* * 'anonymous': will hash user IDs and user logins before collecting them
* * 'anon': alias for 'anonymous'
* * 'safe': deprecated alias for 'anonymous'
*
* * 'identification': will collect user IDs and logins without redaction
* * 'ident': alias for 'identification'
* * 'extended': deprecated alias for 'identification'
*
* * 'disabled': will not collect user IDs and logins
*
* Unknown values will be considered as 'disabled'
* @default 'ident'
*/
mode?: 'safe' | 'extended' | 'disabled'
mode?:
'anonymous' | 'anon' | 'safe' |
'identification' | 'ident' | 'extended' |
'disabled'
},
/**
* Configuration for Api Security sampling
Expand Down Expand Up @@ -757,7 +769,7 @@ declare namespace tracer {
*/
maxDepth?: number
}

/**
* Configuration enabling LLM Observability. Enablement is superceded by the DD_LLMOBS_ENABLED environment variable.
*/
Expand Down Expand Up @@ -876,6 +888,15 @@ declare namespace tracer {
*/
trackUserLoginFailureEvent(userId: string, exists: boolean, metadata?: { [key: string]: string }): void

/**
* Links a signup event to the current trace.
* @param {string} userId The user id of the signed-up user
* @param {[key: string]: string} metadata Custom fields to link to the signup event.
*
* @beta This method is in beta and could change in future versions.
*/
trackUserSignupEvent(userId: string, metadata?: { [key: string]: string }): void

/**
* Links a custom event to the current trace.
* @param {string} eventName The name of the event.
Expand Down Expand Up @@ -2226,7 +2247,7 @@ declare namespace tracer {
* Disable LLM Observability tracing.
*/
disable (): void,

/**
* Instruments a function by automatically creating a span activated on its
* scope.
Expand Down Expand Up @@ -2268,10 +2289,10 @@ declare namespace tracer {
/**
* Decorate a function in a javascript runtime that supports function decorators.
* Note that this is **not** supported in the Node.js runtime, but is in TypeScript.
*
*
* In TypeScript, this decorator is only supported in contexts where general TypeScript
* function decorators are supported.
*
*
* @param options Optional LLM Observability span options.
*/
decorate (options: llmobs.LLMObsNamelessSpanOptions): any
Expand All @@ -2288,7 +2309,7 @@ declare namespace tracer {
/**
* Sets inputs, outputs, tags, metadata, and metrics as provided for a given LLM Observability span.
* Note that with the exception of tags, this method will override any existing values for the provided fields.
*
*
* For example:
* ```javascript
* llmobs.trace({ kind: 'llm', name: 'myLLM', modelName: 'gpt-4o', modelProvider: 'openai' }, () => {
Expand All @@ -2301,7 +2322,7 @@ declare namespace tracer {
* })
* })
* ```
*
*
* @param span The span to annotate (defaults to the current LLM Observability span if not provided)
* @param options An object containing the inputs, outputs, tags, metadata, and metrics to set on the span.
*/
Expand Down Expand Up @@ -2477,14 +2498,14 @@ declare namespace tracer {
* LLM Observability span kind. One of `agent`, `workflow`, `task`, `tool`, `retrieval`, `embedding`, or `llm`.
*/
kind: llmobs.spanKind,

/**
* The ID of the underlying user session. Required for tracking sessions.
*/
sessionId?: string,

/**
* The name of the ML application that the agent is orchestrating.
* The name of the ML application that the agent is orchestrating.
* If not provided, the default value will be set to mlApp provided during initalization, or `DD_LLMOBS_ML_APP`.
*/
mlApp?: string,
Expand Down
1 change: 1 addition & 0 deletions packages/datadog-instrumentations/src/helpers/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ module.exports = {
oracledb: () => require('../oracledb'),
openai: () => require('../openai'),
paperplane: () => require('../paperplane'),
passport: () => require('../passport'),
'passport-http': () => require('../passport-http'),
'passport-local': () => require('../passport-local'),
pg: () => require('../pg'),
Expand Down
16 changes: 2 additions & 14 deletions packages/datadog-instrumentations/src/passport-http.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
'use strict'

const shimmer = require('../../datadog-shimmer')
const { addHook } = require('./helpers/instrument')
const { wrapVerify } = require('./passport-utils')
const { strategyHook } = require('./passport-utils')

addHook({
name: 'passport-http',
file: 'lib/passport-http/strategies/basic.js',
versions: ['>=0.3.0']
}, BasicStrategy => {
return shimmer.wrapFunction(BasicStrategy, BasicStrategy => function () {
const type = 'http'

if (typeof arguments[0] === 'function') {
arguments[0] = wrapVerify(arguments[0], false, type)
} else {
arguments[1] = wrapVerify(arguments[1], (arguments[0] && arguments[0].passReqToCallback), type)
}
return BasicStrategy.apply(this, arguments)
})
})
}, strategyHook)
16 changes: 2 additions & 14 deletions packages/datadog-instrumentations/src/passport-local.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
'use strict'

const shimmer = require('../../datadog-shimmer')
const { addHook } = require('./helpers/instrument')
const { wrapVerify } = require('./passport-utils')
const { strategyHook } = require('./passport-utils')

addHook({
name: 'passport-local',
file: 'lib/strategy.js',
versions: ['>=1.0.0']
}, Strategy => {
return shimmer.wrapFunction(Strategy, Strategy => function () {
const type = 'local'

if (typeof arguments[0] === 'function') {
arguments[0] = wrapVerify(arguments[0], false, type)
} else {
arguments[1] = wrapVerify(arguments[1], (arguments[0] && arguments[0].passReqToCallback), type)
}
return Strategy.apply(this, arguments)
})
})
}, strategyHook)
61 changes: 42 additions & 19 deletions packages/datadog-instrumentations/src/passport-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,56 @@ const { channel } = require('./helpers/instrument')

const passportVerifyChannel = channel('datadog:passport:verify:finish')

function wrapVerifiedAndPublish (username, password, verified, type) {
if (!passportVerifyChannel.hasSubscribers) {
return verified
}
function wrapVerifiedAndPublish (username, verified) {
return shimmer.wrapFunction(verified, function wrapVerify (verified) {
return function wrappedVerified (err, user) {
// if there is an error, it's neither an auth success nor a failure
if (!err) {
const abortController = new AbortController()

passportVerifyChannel.publish({ login: username, user, success: !!user, abortController })

if (abortController.signal.aborted) return
}

// eslint-disable-next-line n/handle-callback-err
return shimmer.wrapFunction(verified, verified => function (err, user, info) {
const credentials = { type, username }
passportVerifyChannel.publish({ credentials, user })
return verified.apply(this, arguments)
return verified.apply(this, arguments)
}
})
}

function wrapVerify (verify, passReq, type) {
if (passReq) {
return function (req, username, password, verified) {
arguments[3] = wrapVerifiedAndPublish(username, password, verified, type)
return verify.apply(this, arguments)
function wrapVerify (verify) {
return function wrappedVerify (req, username, password, verified) {
if (passportVerifyChannel.hasSubscribers) {
// replace the callback with our own wrapper to get the result
// if we ever need the type of strategy, we can get it from this.name
if (this._passReqToCallback) {
arguments[3] = wrapVerifiedAndPublish(arguments[1], arguments[3])
} else {
arguments[2] = wrapVerifiedAndPublish(arguments[0], arguments[2])
}
}
} else {
return function (username, password, verified) {
arguments[2] = wrapVerifiedAndPublish(username, password, verified, type)
return verify.apply(this, arguments)

return verify.apply(this, arguments)
}
}

function wrapStrategy (Strategy) {
return function wrappedStrategy () {
// verify function can be either the first or second argument
if (typeof arguments[0] === 'function') {
arguments[0] = wrapVerify(arguments[0])
} else {
arguments[1] = wrapVerify(arguments[1])
}

return Strategy.apply(this, arguments)
}
}

function strategyHook (Strategy) {
return shimmer.wrapFunction(Strategy, wrapStrategy)
}

module.exports = {
wrapVerify
strategyHook
}
85 changes: 85 additions & 0 deletions packages/datadog-instrumentations/src/passport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use strict'

const shimmer = require('../../datadog-shimmer')
const { addHook } = require('./helpers/instrument')

/* TODO: test with:
passport-jwt JWTs
can be used both for login events, or as a session, that complicates things it think
maybe instrument this lib directly, and ofc only send the events after it was verified
@nestjs/passport
pasport-local
passport-oauth2
passport-google-oauth20
passport-custom
passport-http
passport-http-bearer
koa-passport
*/

function wrapDone (done) {
// eslint-disable-next-line n/handle-callback-err
return function wrappedDone (err, user) {
if (user) {
const abortController = new AbortController()

// express-session middleware sets req.sessionID, it's required to use passport sessions anyway so might as well use it ?

Check failure on line 26 in packages/datadog-instrumentations/src/passport.js

View workflow job for this annotation

GitHub Actions / lint

This line has a length of 127. Maximum allowed is 120
// what if session IDs are using rolling sessions or always changing or something idk ?
channel.publish({ req, user, sessionId: req.sessionID, abortController })

Check failure on line 28 in packages/datadog-instrumentations/src/passport.js

View workflow job for this annotation

GitHub Actions / lint

'channel' is not defined

Check failure on line 28 in packages/datadog-instrumentations/src/passport.js

View workflow job for this annotation

GitHub Actions / lint

'req' is not defined

Check failure on line 28 in packages/datadog-instrumentations/src/passport.js

View workflow job for this annotation

GitHub Actions / lint

'req' is not defined

if (abortController.signal.aborted) return
}

return done.apply(this, arguments)
}
}

function wrapDeserializeUser (deserializeUser) {
return function wrappedDeserializeUser (fn, req, done) {
if (typeof req === 'function') {
done = req
// req = storage.getStore().get('req')
arguments[1] = wrapDone(done)
} else {
arguments[2] = wrapDone(done)
}

return deserializeUser.apply(this, arguments)
}
}


Check failure on line 51 in packages/datadog-instrumentations/src/passport.js

View workflow job for this annotation

GitHub Actions / lint

More than 1 blank line not allowed
const { block } = require('../../dd-trace/src/appsec/blocking')
const { getRootSpan } = require('../../dd-trace/src/appsec/sdk/utils')

addHook({
name: 'passport',
file: 'lib/authenticator.js',
versions: ['>=0.3.0'] // TODO
}, Authenticator => {
shimmer.wrap(Authenticator.prototype, 'deserializeUser', wrapDeserializeUser)

shimmer.wrap(Authenticator.prototype, 'authenticate', function wrapAuthenticate (authenticate) {
return function wrappedAuthenticate (name) {
const middleware = authenticate.apply(this, arguments)

const strategy = this._strategy(name)

strategy._verify

return function wrappedMiddleware (req, res, next) {
return middleware(req, res, function wrappedNext (err) {

Check failure on line 71 in packages/datadog-instrumentations/src/passport.js

View workflow job for this annotation

GitHub Actions / lint

Expected error to be handled
console.log('NEW', req.user)

Check failure on line 72 in packages/datadog-instrumentations/src/passport.js

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
if (req.user?.name === 'bitch') {

Check failure on line 73 in packages/datadog-instrumentations/src/passport.js

View workflow job for this annotation

GitHub Actions / lint

Block must not be padded by blank lines

return block(req, res, getRootSpan(global._ddtrace))
}

return next.apply(this, arguments)
})
}
}
})

return Authenticator
})
6 changes: 5 additions & 1 deletion packages/dd-trace/src/appsec/addresses.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict'

// TODO: reorder all this, it's a mess
module.exports = {
HTTP_INCOMING_BODY: 'server.request.body',
HTTP_INCOMING_QUERY: 'server.request.query',
Expand All @@ -20,6 +21,8 @@ module.exports = {
HTTP_CLIENT_IP: 'http.client_ip',

USER_ID: 'usr.id',
USER_LOGIN: 'usr.login',

WAF_CONTEXT_PROCESSOR: 'waf.context.processor',

HTTP_OUTGOING_URL: 'server.io.net.url',
Expand All @@ -29,5 +32,6 @@ module.exports = {
DB_SYSTEM: 'server.db.system',

LOGIN_SUCCESS: 'server.business_logic.users.login.success',
LOGIN_FAILURE: 'server.business_logic.users.login.failure'
LOGIN_FAILURE: 'server.business_logic.users.login.failure',
SIGNUP: 'server.business_logic.users.signup'
}
Loading
Loading