Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,21 @@
}

const platformSettings = ctx.platformSettings as StackOverflowPlatformSettings
const key = platformSettings.key
const key = platformSettings?.key

ctx.log.info(
{

Check failure on line 54 in services/libs/integrations/src/integrations/stackoverflow/api/getQuestions.ts

View workflow job for this annotation

GitHub Actions / lint-format-services

Delete `·`
hasPlatformSettings: !!platformSettings,

Check failure on line 55 in services/libs/integrations/src/integrations/stackoverflow/api/getQuestions.ts

View workflow job for this annotation

GitHub Actions / lint-format-services

Delete `·`
hasKey: !!key,
keyPrefix: key ? key.substring(0, 5) : 'undefined',
},
'StackOverflow: platformSettings check (tags)',
)

if (!key) {
ctx.log.error('StackOverflow: No API key found in platformSettings!')
throw new Error('StackOverflow API key not configured')
}

// Get access token from Nango
let accessToken = null
Expand Down Expand Up @@ -83,7 +97,22 @@
params,
}

ctx.log.info(
{ url: config.url, params: { ...config.params, key: '***' } },
'StackOverflow: Making API call (tags)',
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OAuth access token exposed in log output

The new logging statement masks the API key with '***' but fails to mask the access_token which was added to params at lines 90-92. When an OAuth token is obtained from Nango, it gets logged in plaintext via the { ...config.params, key: '***' } spread. The access_token property remains exposed in the logged object, potentially allowing anyone with log access to obtain valid authentication credentials.

Additional Locations (1)

Fix in Cursor Fix in Web


const response: StackOverflowQuestionsResponse = (await axios(config)).data

ctx.log.info(
{

Check failure on line 108 in services/libs/integrations/src/integrations/stackoverflow/api/getQuestions.ts

View workflow job for this annotation

GitHub Actions / lint-format-services

Delete `·`
itemsCount: response.items?.length ?? 0,

Check failure on line 109 in services/libs/integrations/src/integrations/stackoverflow/api/getQuestions.ts

View workflow job for this annotation

GitHub Actions / lint-format-services

Delete `·`
hasMore: response.has_more,
quotaRemaining: response.quota_remaining,
},
'StackOverflow: API response received (tags)',
)

const backoff = response.backoff
if (backoff) {
if (backoff <= 2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,21 @@
}

const platformSettings = ctx.platformSettings as StackOverflowPlatformSettings
const key = platformSettings.key
const key = platformSettings?.key

ctx.log.info(
{

Check failure on line 65 in services/libs/integrations/src/integrations/stackoverflow/api/getQuestionsByKeywords.ts

View workflow job for this annotation

GitHub Actions / lint-format-services

Delete `·`
hasPlatformSettings: !!platformSettings,

Check failure on line 66 in services/libs/integrations/src/integrations/stackoverflow/api/getQuestionsByKeywords.ts

View workflow job for this annotation

GitHub Actions / lint-format-services

Delete `·`
hasKey: !!key,
keyPrefix: key ? key.substring(0, 5) : 'undefined',
},
'StackOverflow: platformSettings check',
)

if (!key) {
ctx.log.error('StackOverflow: No API key found in platformSettings!')
throw new Error('StackOverflow API key not configured')
}

const params: Record<string, unknown> = {
page: input.page,
Expand All @@ -83,7 +97,22 @@
params,
}

ctx.log.info(
{ url: config.url, params: { ...config.params, key: '***' } },
'StackOverflow: Making API call',
)

const response: StackOverflowQuestionsResponse = (await axios(config)).data

ctx.log.info(
{

Check failure on line 108 in services/libs/integrations/src/integrations/stackoverflow/api/getQuestionsByKeywords.ts

View workflow job for this annotation

GitHub Actions / lint-format-services

Delete `·`
itemsCount: response.items?.length ?? 0,

Check failure on line 109 in services/libs/integrations/src/integrations/stackoverflow/api/getQuestionsByKeywords.ts

View workflow job for this annotation

GitHub Actions / lint-format-services

Delete `·`
hasMore: response.has_more,
quotaRemaining: response.quota_remaining,
},
'StackOverflow: API response received',
)

const backoff = response.backoff
if (backoff) {
if (backoff <= 2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,55 @@ import {
const handler: GenerateStreamsHandler = async (ctx) => {
const settings = ctx.integration.settings as IStackOverflowIntegrationSettings

ctx.log.info(
{
integrationId: ctx.integration.id,
tagsCount: settings?.tags?.length ?? 0,
keywordsCount: settings?.keywords?.length ?? 0,
tags: settings?.tags,
keywords: settings?.keywords,
},
'StackOverflow generateStreams: starting',
)

if (settings.keywords.length === 0 && settings.tags.length === 0) {
await ctx.abortRunWithError('No keywords or tags configured!')
return
}

let streamsPublished = 0

if (settings.tags.length > 0) {
for (const tag of settings.tags) {
ctx.log.info({ tag }, 'StackOverflow: publishing tag stream')
await ctx.publishStream<IStackOverflowTagStreamData>(
`${StackOverflowRootStream.QUESTIONS_BY_TAG}:${tag}:${1}`,
{
tags: [tag],
page: 1,
},
)
streamsPublished++
}
}
if (settings.keywords.length > 0) {
for (const keyword of settings.keywords) {
ctx.log.info({ keyword }, 'StackOverflow: publishing keyword stream')
await ctx.publishStream<IStackOverflowKeywordStreamData>(
`${StackOverflowRootStream.QUESTIONS_BY_KEYWORD}:${keyword}:${1}`,
{
keyword,
page: 1,
},
)
streamsPublished++
}
}

ctx.log.info(
{ streamsPublished, integrationId: ctx.integration.id },
'StackOverflow generateStreams: completed',
)
}

export default handler
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import processStream from './processStream'
const descriptor: IIntegrationDescriptor = {
type: PlatformType.STACKOVERFLOW,
memberAttributes: STACKOVERFLOW_MEMBER_ATTRIBUTES,
checkEvery: 360, // 6 hours
checkEvery: 60, // 1 hour
generateStreams,
processStream,
processData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ const processTagStream: ProcessStreamHandler = async (ctx) => {

const questions = response.items

ctx.log.info(
{ tags, page, questionsCount: questions.length, hasMore: response.has_more },
'StackOverflow: tag stream API response',
)

if (questions.length === 0) {
return
}
Expand Down Expand Up @@ -110,6 +115,11 @@ const processKeywordStream: ProcessStreamHandler = async (ctx) => {

const questions = response.items

ctx.log.info(
{ keyword, page, questionsCount: questions.length, hasMore: response.has_more },
'StackOverflow: keyword stream API response',
)

if (questions.length === 0) {
return
}
Expand Down Expand Up @@ -179,6 +189,11 @@ const processAnswerStream: ProcessStreamHandler = async (ctx) => {

const answers = response.items

ctx.log.info(
{ questionId, page, answersCount: answers.length, hasMore: response.has_more, tag, keyword },
'StackOverflow: answer stream API response',
)

if (answers.length === 0) {
return
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const STACKOVERFLOW_MAX_RETROSPECT_IN_HOURS = 7
export const STACKOVERFLOW_MAX_RETROSPECT_IN_HOURS = 5
export const STACKOVERFLOW_LAST_MAX_PAGES = 20

export enum StackOverflowActivityType {
Expand Down
Loading