Socket
Socket
Sign inDemoInstall

dd-trace

Package Overview
Dependencies
75
Maintainers
1
Versions
552
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 4.35.0 to 4.36.0

2

package.json
{
"name": "dd-trace",
"version": "4.35.0",
"version": "4.36.0",
"description": "Datadog APM tracing client for JavaScript",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -10,9 +10,14 @@ 'use strict'

const req = new Request(input, init)
const headers = req.headers
const ctx = { req, headers }
if (input instanceof Request) {
const ctx = { req: input }
return ch.tracePromise(() => fetch.call(this, req, { headers: ctx.headers }), ctx)
return ch.tracePromise(() => fetch.call(this, input, init), ctx)
} else {
const req = new Request(input, init)
const ctx = { req }
return ch.tracePromise(() => fetch.call(this, req), ctx)
}
}
}
}

@@ -13,2 +13,94 @@ 'use strict'

const V4_PACKAGE_SHIMS = [
{
file: 'resources/chat/completions.js',
targetClass: 'Completions',
baseResource: 'chat.completions',
methods: ['create']
},
{
file: 'resources/completions.js',
targetClass: 'Completions',
baseResource: 'completions',
methods: ['create']
},
{
file: 'resources/embeddings.js',
targetClass: 'Embeddings',
baseResource: 'embeddings',
methods: ['create']
},
{
file: 'resources/files.js',
targetClass: 'Files',
baseResource: 'files',
methods: ['create', 'del', 'list', 'retrieve']
},
{
file: 'resources/files.js',
targetClass: 'Files',
baseResource: 'files',
methods: ['retrieveContent'],
versions: ['>=4.0.0 <4.17.1']
},
{
file: 'resources/files.js',
targetClass: 'Files',
baseResource: 'files',
methods: ['content'], // replaced `retrieveContent` in v4.17.1
versions: ['>=4.17.1']
},
{
file: 'resources/images.js',
targetClass: 'Images',
baseResource: 'images',
methods: ['createVariation', 'edit', 'generate']
},
{
file: 'resources/fine-tuning/jobs/jobs.js',
targetClass: 'Jobs',
baseResource: 'fine_tuning.jobs',
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
versions: ['>=4.34.0'] // file location changed in 4.34.0
},
{
file: 'resources/fine-tuning/jobs.js',
targetClass: 'Jobs',
baseResource: 'fine_tuning.jobs',
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
versions: ['>=4.1.0 <4.34.0']
},
{
file: 'resources/fine-tunes.js', // deprecated after 4.1.0
targetClass: 'FineTunes',
baseResource: 'fine-tune',
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
versions: ['>=4.0.0 <4.1.0']
},
{
file: 'resources/models.js',
targetClass: 'Models',
baseResource: 'models',
methods: ['del', 'list', 'retrieve']
},
{
file: 'resources/moderations.js',
targetClass: 'Moderations',
baseResource: 'moderations',
methods: ['create']
},
{
file: 'resources/audio/transcriptions.js',
targetClass: 'Transcriptions',
baseResource: 'audio.transcriptions',
methods: ['create']
},
{
file: 'resources/audio/translations.js',
targetClass: 'Translations',
baseResource: 'audio.translations',
methods: ['create']
}
]
addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0 <4'] }, exports => {

@@ -52,1 +144,58 @@ const methodNames = Object.getOwnPropertyNames(exports.OpenAIApi.prototype)

})
for (const shim of V4_PACKAGE_SHIMS) {
const { file, targetClass, baseResource, methods } = shim
addHook({ name: 'openai', file, versions: shim.versions || ['>=4'] }, exports => {
const targetPrototype = exports[targetClass].prototype
for (const methodName of methods) {
shimmer.wrap(targetPrototype, methodName, methodFn => function () {
if (!startCh.hasSubscribers) {
return methodFn.apply(this, arguments)
}
const client = this._client || this.client
startCh.publish({
methodName: `${baseResource}.${methodName}`,
args: arguments,
basePath: client.baseURL,
apiKey: client.apiKey
})
const apiProm = methodFn.apply(this, arguments)
// wrapping `parse` avoids problematic wrapping of `then` when trying to call
// `withResponse` in userland code after. This way, we can return the whole `APIPromise`
shimmer.wrap(apiProm, 'parse', origApiPromParse => function () {
return origApiPromParse.apply(this, arguments)
// the original response is wrapped in a promise, so we need to unwrap it
.then(body => Promise.all([this.responsePromise, body]))
.then(([{ response, options }, body]) => {
finishCh.publish({
headers: response.headers,
body,
path: response.url,
method: options.method
})
return body
})
.catch(err => {
errorCh.publish({ err })
throw err
})
.finally(() => {
// maybe we don't want to unwrap here in case the promise is re-used?
// other hand: we want to avoid resource leakage
shimmer.unwrap(apiProm, 'parse')
})
})
return apiProm
})
}
return exports
})
}

@@ -20,4 +20,7 @@ 'use strict'

ctx.headers = headers
ctx.req = new globalThis.Request(req, { headers })
for (const name in headers) {
if (!req.headers.has(name)) {
req.headers.set(name, headers[name])
}
}

@@ -24,0 +27,0 @@ return store

@@ -87,3 +87,3 @@ 'use strict'

// createChatCompletion, createCompletion, createImage, createImageEdit, createTranscription, createTranslation
if ('prompt' in payload) {
if (payload.prompt) {
const prompt = payload.prompt

@@ -103,3 +103,3 @@ store.prompt = prompt

// createEdit, createEmbedding, createModeration
if ('input' in payload) {
if (payload.input) {
const normalized = normalizeStringOrTokenArray(payload.input, false)

@@ -119,2 +119,4 @@ tags['openai.request.input'] = truncateText(normalized)

case 'createFineTune':
case 'fine_tuning.jobs.create':
case 'fine-tune.create':
createFineTuneRequestExtraction(tags, payload)

@@ -124,4 +126,7 @@ break

case 'createImage':
case 'images.generate':
case 'createImageEdit':
case 'images.edit':
case 'createImageVariation':
case 'images.createVariation':
commonCreateImageRequestExtraction(tags, payload, store)

@@ -131,2 +136,3 @@ break

case 'createChatCompletion':
case 'chat.completions.create':
createChatCompletionRequestExtraction(tags, payload, store)

@@ -136,3 +142,5 @@ break

case 'createFile':
case 'files.create':
case 'retrieveFile':
case 'files.retrieve':
commonFileRequestExtraction(tags, payload)

@@ -142,3 +150,5 @@ break

case 'createTranscription':
case 'audio.transcriptions.create':
case 'createTranslation':
case 'audio.translations.create':
commonCreateAudioRequestExtraction(tags, payload, store)

@@ -148,2 +158,3 @@ break

case 'retrieveModel':
case 'models.retrieve':
retrieveModelRequestExtraction(tags, payload)

@@ -153,5 +164,12 @@ break

case 'listFineTuneEvents':
case 'fine_tuning.jobs.listEvents':
case 'fine-tune.listEvents':
case 'retrieveFineTune':
case 'fine_tuning.jobs.retrieve':
case 'fine-tune.retrieve':
case 'deleteModel':
case 'models.del':
case 'cancelFineTune':
case 'fine_tuning.jobs.cancel':
case 'fine-tune.cancel':
commonLookupFineTuneRequestExtraction(tags, payload)

@@ -161,2 +179,3 @@ break

case 'createEdit':
case 'edits.create':
createEditRequestExtraction(tags, payload, store)

@@ -170,2 +189,6 @@ break

finish ({ headers, body, method, path }) {
if (headers.constructor.name === 'Headers') {
headers = Object.fromEntries(headers)
}
const span = this.activeSpan

@@ -179,2 +202,7 @@ const methodName = span._spanContext._tags['resource.name']

if (path.startsWith('https://') || path.startsWith('http://')) {
// basic checking for if the path was set as a full URL
// not using a full regex as it will likely be "https://api.openai.com/..."
path = new URL(path).pathname
}
const endpoint = lookupOperationEndpoint(methodName, path)

@@ -184,3 +212,3 @@

'openai.request.endpoint': endpoint,
'openai.request.method': method,
'openai.request.method': method.toUpperCase(),

@@ -236,3 +264,3 @@ 'openai.organization.id': body.organization_id, // only available in fine-tunes endpoints

if (body && ('usage' in body)) {
if (body && body.usage) {
const promptTokens = body.usage.prompt_tokens

@@ -245,15 +273,15 @@ const completionTokens = body.usage.completion_tokens

if ('x-ratelimit-limit-requests' in headers) {
if (headers['x-ratelimit-limit-requests']) {
this.metrics.gauge('openai.ratelimit.requests', Number(headers['x-ratelimit-limit-requests']), tags)
}
if ('x-ratelimit-remaining-requests' in headers) {
if (headers['x-ratelimit-remaining-requests']) {
this.metrics.gauge('openai.ratelimit.remaining.requests', Number(headers['x-ratelimit-remaining-requests']), tags)
}
if ('x-ratelimit-limit-tokens' in headers) {
if (headers['x-ratelimit-limit-tokens']) {
this.metrics.gauge('openai.ratelimit.tokens', Number(headers['x-ratelimit-limit-tokens']), tags)
}
if ('x-ratelimit-remaining-tokens' in headers) {
if (headers['x-ratelimit-remaining-tokens']) {
this.metrics.gauge('openai.ratelimit.remaining.tokens', Number(headers['x-ratelimit-remaining-tokens']), tags)

@@ -293,6 +321,6 @@ }

const message = payload.messages[i]
tags[`openai.request.${i}.content`] = truncateText(message.content)
tags[`openai.request.${i}.role`] = message.role
tags[`openai.request.${i}.name`] = message.name
tags[`openai.request.${i}.finish_reason`] = message.finish_reason
tags[`openai.request.messages.${i}.content`] = truncateText(message.content)
tags[`openai.request.messages.${i}.role`] = message.role
tags[`openai.request.messages.${i}.name`] = message.name
tags[`openai.request.messages.${i}.finish_reason`] = message.finish_reason
}

@@ -303,4 +331,5 @@ }

// createImageEdit, createImageVariation
if (payload.file && typeof payload.file === 'object' && payload.file.path) {
const file = path.basename(payload.file.path)
const img = payload.file || payload.image
if (img && typeof img === 'object' && img.path) {
const file = path.basename(img.path)
tags['openai.request.image'] = file

@@ -325,2 +354,3 @@ store.file = file

case 'createModeration':
case 'moderations.create':
createModerationResponseExtraction(tags, body)

@@ -330,4 +360,7 @@ break

case 'createCompletion':
case 'completions.create':
case 'createChatCompletion':
case 'chat.completions.create':
case 'createEdit':
case 'edits.create':
commonCreateResponseExtraction(tags, body, store)

@@ -337,4 +370,9 @@ break

case 'listFiles':
case 'files.list':
case 'listFineTunes':
case 'fine_tuning.jobs.list':
case 'fine-tune.list':
case 'listFineTuneEvents':
case 'fine_tuning.jobs.listEvents':
case 'fine-tune.listEvents':
commonListCountResponseExtraction(tags, body)

@@ -344,2 +382,3 @@ break

case 'createEmbedding':
case 'embeddings.create':
createEmbeddingResponseExtraction(tags, body)

@@ -349,3 +388,5 @@ break

case 'createFile':
case 'files.create':
case 'retrieveFile':
case 'files.retrieve':
createRetrieveFileResponseExtraction(tags, body)

@@ -355,2 +396,3 @@ break

case 'deleteFile':
case 'files.del':
deleteFileResponseExtraction(tags, body)

@@ -360,2 +402,4 @@ break

case 'downloadFile':
case 'files.retrieveContent':
case 'files.content':
downloadFileResponseExtraction(tags, body)

@@ -365,4 +409,10 @@ break

case 'createFineTune':
case 'fine_tuning.jobs.create':
case 'fine-tune.create':
case 'retrieveFineTune':
case 'fine_tuning.jobs.retrieve':
case 'fine-tune.retrieve':
case 'cancelFineTune':
case 'fine_tuning.jobs.cancel':
case 'fine-tune.cancel':
commonFineTuneResponseExtraction(tags, body)

@@ -372,3 +422,5 @@ break

case 'createTranscription':
case 'audio.transcriptions.create':
case 'createTranslation':
case 'audio.translations.create':
createAudioResponseExtraction(tags, body)

@@ -378,4 +430,7 @@ break

case 'createImage':
case 'images.generate':
case 'createImageEdit':
case 'images.edit':
case 'createImageVariation':
case 'images.createVariation':
commonImageResponseExtraction(tags, body)

@@ -385,2 +440,3 @@ break

case 'listModels':
case 'models.list':
listModelsResponseExtraction(tags, body)

@@ -390,2 +446,3 @@ break

case 'retrieveModel':
case 'models.retrieve':
retrieveModelResponseExtraction(tags, body)

@@ -463,11 +520,15 @@ break

tags['openai.response.fine_tuned_model'] = body.fine_tuned_model
if (body.hyperparams) {
tags['openai.response.hyperparams.n_epochs'] = body.hyperparams.n_epochs
tags['openai.response.hyperparams.batch_size'] = body.hyperparams.batch_size
tags['openai.response.hyperparams.prompt_loss_weight'] = body.hyperparams.prompt_loss_weight
tags['openai.response.hyperparams.learning_rate_multiplier'] = body.hyperparams.learning_rate_multiplier
const hyperparams = body.hyperparams || body.hyperparameters
const hyperparamsKey = body.hyperparams ? 'hyperparams' : 'hyperparameters'
if (hyperparams) {
tags[`openai.response.${hyperparamsKey}.n_epochs`] = hyperparams.n_epochs
tags[`openai.response.${hyperparamsKey}.batch_size`] = hyperparams.batch_size
tags[`openai.response.${hyperparamsKey}.prompt_loss_weight`] = hyperparams.prompt_loss_weight
tags[`openai.response.${hyperparamsKey}.learning_rate_multiplier`] = hyperparams.learning_rate_multiplier
}
tags['openai.response.training_files_count'] = defensiveArrayLength(body.training_files)
tags['openai.response.training_files_count'] = defensiveArrayLength(body.training_files || body.training_file)
tags['openai.response.result_files_count'] = defensiveArrayLength(body.result_files)
tags['openai.response.validation_files_count'] = defensiveArrayLength(body.validation_files)
tags['openai.response.validation_files_count'] = defensiveArrayLength(body.validation_files || body.validation_file)
tags['openai.response.updated_at'] = body.updated_at

@@ -561,14 +622,27 @@ tags['openai.response.status'] = body.status

for (let i = 0; i < body.choices.length; i++) {
const choice = body.choices[i]
tags[`openai.response.choices.${i}.finish_reason`] = choice.finish_reason
tags[`openai.response.choices.${i}.logprobs`] = ('logprobs' in choice) ? 'returned' : undefined
tags[`openai.response.choices.${i}.text`] = truncateText(choice.text)
for (let choiceIdx = 0; choiceIdx < body.choices.length; choiceIdx++) {
const choice = body.choices[choiceIdx]
// logprobs can be nullm and we still want to tag it as 'returned' even when set to 'null'
const specifiesLogProb = Object.keys(choice).indexOf('logprobs') !== -1
tags[`openai.response.choices.${choiceIdx}.finish_reason`] = choice.finish_reason
tags[`openai.response.choices.${choiceIdx}.logprobs`] = specifiesLogProb ? 'returned' : undefined
tags[`openai.response.choices.${choiceIdx}.text`] = truncateText(choice.text)
// createChatCompletion only
if ('message' in choice) {
if (choice.message) {
const message = choice.message
tags[`openai.response.choices.${i}.message.role`] = message.role
tags[`openai.response.choices.${i}.message.content`] = truncateText(message.content)
tags[`openai.response.choices.${i}.message.name`] = truncateText(message.name)
tags[`openai.response.choices.${choiceIdx}.message.role`] = message.role
tags[`openai.response.choices.${choiceIdx}.message.content`] = truncateText(message.content)
tags[`openai.response.choices.${choiceIdx}.message.name`] = truncateText(message.name)
if (message.tool_calls) {
const toolCalls = message.tool_calls
for (let toolIdx = 0; toolIdx < toolCalls.length; toolIdx++) {
tags[`openai.response.choices.${choiceIdx}.message.tool_calls.${toolIdx}.name`] =
toolCalls[toolIdx].function.name
tags[`openai.response.choices.${choiceIdx}.message.tool_calls.${toolIdx}.arguments`] =
toolCalls[toolIdx].function.arguments
}
}
}

@@ -611,6 +685,19 @@ }

case 'downloadFile':
case 'files.retrieveContent':
case 'files.content':
return { file: body }
}
return typeof body === 'object' ? body : {}
const type = typeof body
if (type === 'string') {
try {
return JSON.parse(body)
} catch {
return body
}
} else if (type === 'object') {
return body
} else {
return {}
}
}

@@ -622,20 +709,35 @@

case 'deleteModel':
case 'models.del':
case 'retrieveModel':
case 'models.retrieve':
return '/v1/models/*'
case 'deleteFile':
case 'files.del':
case 'retrieveFile':
case 'files.retrieve':
return '/v1/files/*'
case 'downloadFile':
case 'files.retrieveContent':
case 'files.content':
return '/v1/files/*/content'
case 'retrieveFineTune':
case 'fine-tune.retrieve':
return '/v1/fine-tunes/*'
case 'fine_tuning.jobs.retrieve':
return '/v1/fine_tuning/jobs/*'
case 'listFineTuneEvents':
case 'fine-tune.listEvents':
return '/v1/fine-tunes/*/events'
case 'fine_tuning.jobs.listEvents':
return '/v1/fine_tuning/jobs/*/events'
case 'cancelFineTune':
case 'fine-tune.cancel':
return '/v1/fine-tunes/*/cancel'
case 'fine_tuning.jobs.cancel':
return '/v1/fine_tuning/jobs/*/cancel'
}

@@ -654,4 +756,8 @@

case 'listModels':
case 'models.list':
case 'listFiles':
case 'files.list':
case 'listFineTunes':
case 'fine_tuning.jobs.list':
case 'fine-tune.list':
// no argument

@@ -661,2 +767,3 @@ return {}

case 'retrieveModel':
case 'models.retrieve':
return { id: args[0] }

@@ -671,7 +778,13 @@

case 'deleteFile':
case 'files.del':
case 'retrieveFile':
case 'files.retrieve':
case 'downloadFile':
case 'files.retrieveContent':
case 'files.content':
return { file_id: args[0] }
case 'listFineTuneEvents':
case 'fine_tuning.jobs.listEvents':
case 'fine-tune.listEvents':
return {

@@ -683,4 +796,9 @@ fine_tune_id: args[0],

case 'retrieveFineTune':
case 'fine_tuning.jobs.retrieve':
case 'fine-tune.retrieve':
case 'deleteModel':
case 'models.del':
case 'cancelFineTune':
case 'fine_tuning.jobs.cancel':
case 'fine-tune.cancel':
return { fine_tune_id: args[0] }

@@ -742,5 +860,14 @@

function defensiveArrayLength (maybeArray) {
return Array.isArray(maybeArray) ? maybeArray.length : undefined
if (maybeArray) {
if (Array.isArray(maybeArray)) {
return maybeArray.length
} else {
// case of a singular item (ie body.training_file vs body.training_files)
return 1
}
}
return undefined
}
module.exports = OpenApiPlugin

@@ -7,3 +7,3 @@ 'use strict'

function taintObject (iastContext, object, type, keyTainting, keyType) {
function taintObject (iastContext, object, type) {
let result = object

@@ -26,5 +26,2 @@ const transactionId = iastContext?.[IAST_TRANSACTION_ID]

result = tainted
} else if (keyTainting && key) {
const taintedProperty = TaintedUtils.newTaintedString(transactionId, key, property, keyType)
parent[taintedProperty] = tainted
} else {

@@ -39,7 +36,2 @@ parent[key] = tainted

}
if (parent && keyTainting && key) {
const taintedProperty = TaintedUtils.newTaintedString(transactionId, key, property, keyType)
parent[taintedProperty] = value
}
}

@@ -46,0 +38,0 @@ } catch (e) {

@@ -98,8 +98,10 @@ 'use strict'

const iastContext = getIastContext(storage.getStore())
taintObject(iastContext, target, HTTP_REQUEST_COOKIE_VALUE, true, HTTP_REQUEST_COOKIE_NAME)
// Prevent tainting cookie names since it leads to taint literal string with same value.
taintObject(iastContext, target, HTTP_REQUEST_COOKIE_VALUE)
}
taintHeaders (headers, iastContext) {
// Prevent tainting header names since it leads to taint literal string with same value.
this.execSource({
handler: () => taintObject(iastContext, headers, HTTP_REQUEST_HEADER_VALUE, true, HTTP_REQUEST_HEADER_NAME),
handler: () => taintObject(iastContext, headers, HTTP_REQUEST_HEADER_VALUE),
tags: REQ_HEADER_TAGS,

@@ -106,0 +108,0 @@ iastContext

@@ -11,3 +11,3 @@ 'use strict'

function enable (config) {
function enable (config, appsec) {
rc = new RemoteConfigManager(config)

@@ -35,3 +35,3 @@ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_CUSTOM_TAGS, true)

if (activation === Activation.ONECLICK) {
enableOrDisableAppsec(action, rcConfig, config)
enableOrDisableAppsec(action, rcConfig, config, appsec)
}

@@ -46,3 +46,3 @@

function enableOrDisableAppsec (action, rcConfig, config) {
function enableOrDisableAppsec (action, rcConfig, config, appsec) {
if (typeof rcConfig.asm?.enabled === 'boolean') {

@@ -58,5 +58,5 @@ let shouldEnable

if (shouldEnable) {
require('..').enable(config)
appsec.enable(config)
} else {
require('..').disable()
appsec.disable()
}

@@ -63,0 +63,0 @@ }

@@ -18,2 +18,17 @@ 'use strict'

class LazyModule {
constructor (provider) {
this.provider = provider
}
enable (...args) {
this.module = this.provider()
this.module.enable(...args)
}
disable () {
this.module?.disable()
}
}
class Tracer extends NoopProxy {

@@ -28,2 +43,8 @@ constructor () {

this._tracingInitialized = false
// these requires must work with esm bundler
this._modules = {
appsec: new LazyModule(() => require('./appsec')),
iast: new LazyModule(() => require('./appsec/iast'))
}
}

@@ -63,3 +84,3 @@

if (config.remoteConfig.enabled && !config.isCiVisibility) {
const rc = remoteConfig.enable(config)
const rc = remoteConfig.enable(config, this._modules.appsec)

@@ -119,5 +140,4 @@ rc.on('APM_TRACING', (action, conf) => {

if (config.tracing !== false) {
// dirty require for now so zero appsec code is executed unless explicitly enabled
if (config.appsec.enabled) {
require('./appsec').enable(config)
this._modules.appsec.enable(config)
}

@@ -130,7 +150,7 @@ if (!this._tracingInitialized) {

if (config.iast.enabled) {
require('./appsec/iast').enable(config, this._tracer)
this._modules.iast.enable(config, this._tracer)
}
} else if (this._tracingInitialized) {
require('./appsec').disable()
require('./appsec/iast').disable()
this._modules.appsec.disable()
this._modules.iast.disable()
}

@@ -137,0 +157,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc