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

Always parse axios errors Axios throws huge error objects which makes logging very verbose and it often includes sensitive data in the logs #1704

Open
wants to merge 2 commits into
base: development
Choose a base branch
from
Open
Changes from 1 commit
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
Next Next commit
chore: parse axios errors always. Axios throws huge error objects whi…
…ch makes logging very verbose and it often includes sensitive data in the logs
  • Loading branch information
niekcandaele committed Oct 20, 2024
commit c4a20580ec663862deeaae3c247341d6ea0ab9d2
5 changes: 3 additions & 2 deletions packages/app-api/src/lib/steamApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import axios, { AxiosError, AxiosInstance } from 'axios';
import { AxiosError, AxiosInstance } from 'axios';
import { config } from '../config.js';
import { addCounterToAxios, errors, logger } from '@takaro/util';
import { createAxios } from '@takaro/apiclient';
import { Redis } from '@takaro/db';
import ms from 'ms';

@@ -45,7 +46,7 @@ class SteamApi {

get client() {
if (!this._client) {
this._client = axios.create({
this._client = createAxios({
baseURL: 'https://api.steampowered.com',
timeout: 10000,
});
6 changes: 3 additions & 3 deletions packages/app-api/src/service/StatsService.ts
Original file line number Diff line number Diff line change
@@ -2,8 +2,8 @@ import { TakaroService } from './Base.js';

import { TakaroDTO, errors, traceableClass } from '@takaro/util';
import { TakaroModel } from '@takaro/db';
import { createAxios } from '@takaro/apiclient';
import { ITakaroRepo, PaginatedOutput } from '../db/base.js';
import { Axios } from 'axios';
import { IsObject } from 'class-validator';
import { config } from '../config.js';
import { CountryStatsInputDTO, EventsCountInputDTO } from '../controllers/StatsController.js';
@@ -16,7 +16,7 @@ export class StatsOutputDTO extends TakaroDTO<StatsOutputDTO> {

@traceableClass('service:stats')
export class StatsService extends TakaroService<TakaroModel, TakaroDTO<void>, TakaroDTO<void>, TakaroDTO<void>> {
private promClient = new Axios({
private promClient = createAxios({
baseURL: config.get('metrics.prometheusUrl'),
});
get repo(): ITakaroRepo<TakaroModel, TakaroDTO<void>, TakaroDTO<void>, TakaroDTO<void>> {
@@ -60,7 +60,7 @@ export class StatsService extends TakaroService<TakaroModel, TakaroDTO<void>, Ta
},
});

const parsed = JSON.parse(response.data);
const parsed = response.data;

if (parsed.status !== 'success') throw new errors.InternalServerError();
if (parsed.data.result.length === 0) return [];
42 changes: 42 additions & 0 deletions packages/lib-apiclient/src/lib/baseAxios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import axios, { AxiosError, CreateAxiosDefaults } from 'axios';

/**
* Creates an opinionated axios instance with defaults
* Includes error handling (Axios by default throws a HUGE error)
* @param opts CreateAxiosDefaults - Configuration for the axios instance
*/
export function createAxios(opts: CreateAxiosDefaults) {
const axiosInstance = axios.create(opts);

// Strip down the error to a more manageable size
axiosInstance.interceptors.response.use(
(response) => response,
(error: AxiosError) => {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
const { status, statusText, headers, data } = error.response;
return Promise.reject({
status,
statusText,
headers,
data,
});
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
return Promise.reject({
message: 'No response received',
});
} else {
// Something happened in setting up the request that triggered an Error
return Promise.reject({
message: error.message,
});
}
},
);

return axiosInstance;
}
5 changes: 3 additions & 2 deletions packages/lib-apiclient/src/lib/baseClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { MetaApi } from '../generated/api.js';
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from 'axios';
import { AxiosError, AxiosInstance, AxiosRequestConfig } from 'axios';
import { createAxios } from './baseAxios.js';

export interface IBaseApiClientConfig {
url: string;
@@ -31,7 +32,7 @@ export class BaseApiClient<T extends IBaseApiClientConfig> {
},
withCredentials: true,
};
this.axios = this.addLoggers(axios.create(axiosConfig));
this.axios = this.addLoggers(createAxios(axiosConfig));

if (this.config.log) this.log = this.config.log;
}
1 change: 1 addition & 0 deletions packages/lib-apiclient/src/main.ts
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ export { AxiosResponse } from 'axios';
export { isAxiosError } from 'axios';
export { AdminClient } from './lib/adminClient.js';
export { Client } from './lib/client.js';
export { createAxios } from './lib/baseAxios.js';

export type ITakaroAPIAxiosResponse<T> = AxiosResponse<T>;
export * from './generated/api.js';
5 changes: 3 additions & 2 deletions packages/lib-auth/src/lib/oryAxiosClient.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import axios, { AxiosError } from 'axios';
import { AxiosError } from 'axios';
import { addCounterToAxios, errors, logger } from '@takaro/util';
import { createAxios } from '@takaro/apiclient';

const log = logger('ory:http');

export function createAxiosClient(baseURL: string) {
const client = axios.create({
const client = createAxios({
baseURL,
headers: {
'Content-Type': 'application/json',
17 changes: 9 additions & 8 deletions packages/lib-gameserver/src/gameservers/7d2d/index.ts
Original file line number Diff line number Diff line change
@@ -122,17 +122,18 @@ export class SevenDaysToDie implements IGameServer {
let reason = 'Unexpected error, this might be a bug';
this.logger.warn('Reachability test requests failed', error);

if (error instanceof Object && 'details' in error) {
if (error instanceof Object && !('status' in error)) {
reason =
'Did not receive a response, please check that the server is running, the IP/port is correct and that it is not firewalled';
if (error.details instanceof Object) {
if ('status' in error.details) {
if (error.details.status === 403 || error.details.status === 401) {
reason = 'Unauthorized, please check that the admin user and token are correct';
}
}
}

if (error instanceof Object && 'status' in error) {
if (error.status === 403 || error.status === 401) {
reason = 'Unauthorized, please check that the admin user and token are correct';
}
} else if (error instanceof Object && 'message' in error && error.message === 'Request timed out') {
}

if (error instanceof Object && 'message' in error && error.message === 'Request timed out') {
reason = 'Request timed out, the server did not respond in the allocated time';
}

31 changes: 4 additions & 27 deletions packages/lib-gameserver/src/gameservers/7d2d/sdtdAPIClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import axios, { Axios, AxiosResponse } from 'axios';
import { Axios, AxiosResponse } from 'axios';
import { SdtdConnectionInfo } from './connectionInfo.js';
import {
CommandResponse,
@@ -7,13 +7,14 @@ import {
PlayerLocation,
StatsResponse,
} from './apiResponses.js';
import { addCounterToAxios, errors } from '@takaro/util';
import { addCounterToAxios } from '@takaro/util';
import { createAxios } from '@takaro/apiclient';

export class SdtdApiClient {
private client: Axios;

constructor(private config: SdtdConnectionInfo) {
this.client = axios.create({
this.client = createAxios({
baseURL: this.url,
});

@@ -28,30 +29,6 @@ export class SdtdApiClient {

return req;
});

this.client.interceptors.response.use(
function (response) {
return response;
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
if (error.response) {
const simplifiedError = new errors.BadRequestError('Axios error', {
extra: 'A request to the 7D2D server failed',
status: error.response.status,
statusText: error.response.statusText,
url: error.config.url,
});
return Promise.reject(simplifiedError);
}
const simplifiedError = new errors.BadRequestError('Axios error', {
extra: 'A request to the 7D2D server failed',
message: error.message,
url: error.config.url,
});
return Promise.reject(simplifiedError);
},
);
}

private get url() {
Loading