Skip to content
Merged
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
4 changes: 0 additions & 4 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,6 @@ const config = defineConfig([
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
rules: {
// These rules could indicate potential bugs in the source code.
// After fixing the source code, remove these so they become errors instead of warnings.
'@typescript-eslint/no-base-to-string': 'warn',

// @typescript-eslint/lines-between-class-members was moved to @stylistic/eslint-plugin, so replaced with lines-between-class-members.
'lines-between-class-members': 'error',
'@typescript-eslint/lines-between-class-members': 'off',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"test:debug": "TZ=utc NODE_OPTIONS='--inspect-brk --experimental-vm-modules' jest --runInBand",
"perf-test": "NODE_OPTIONS=--experimental-vm-modules npx reassure",
"typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc",
"lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=241 --cache --cache-location=node_modules/.cache/eslint",
"lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=154 --cache --cache-location=node_modules/.cache/eslint",
"lint-changed": "NODE_OPTIONS=--max_old_space_size=8192 ./scripts/lintChanged.sh",
"lint-watch": "npx eslint-watch --watch --changed",
"shellcheck": "./scripts/shellCheck.sh",
Expand Down
5 changes: 3 additions & 2 deletions scripts/utils/CLI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* You provide a CLIConfig defining your arguments, then the class will handle parsing argv, type validation, error handling, and help messages.
*/
import type {NonEmptyObject, NonEmptyTuple, ValueOf, Writable} from 'type-fest';
import SafeString from '@src/utils/SafeString';

/**
* A base CLI arg has only a description, which we will use in the help/usage message (built-in to any CLI).
Expand Down Expand Up @@ -292,7 +293,7 @@ class CLI<TConfig extends CLIConfig> {
if (Object.keys(namedArgs).length > 0) {
console.log('Named Arguments:');
for (const [name, spec] of Object.entries(namedArgs)) {
const defaultLabel = spec.default !== undefined ? ` (default: ${String(spec.default)})` : '';
const defaultLabel = spec.default !== undefined ? ` (default: ${SafeString(spec.default)})` : '';
const supersededLabel = spec.supersedes && spec.supersedes.length > 0 ? ` (supersedes: ${spec.supersedes.join(', ')})` : '';
console.log(` --${name.padEnd(20)} ${spec.description}${defaultLabel}${supersededLabel}`);
}
Expand All @@ -302,7 +303,7 @@ class CLI<TConfig extends CLIConfig> {
if (positionalArgs.length > 0) {
console.log('Positional Arguments:');
for (const arg of positionalArgs) {
const defaultLabel = arg.default !== undefined ? ` (default: ${String(arg.default)})` : '';
const defaultLabel = arg.default !== undefined ? ` (default: ${SafeString(arg.default)})` : '';
console.log(` ${arg.name.padEnd(22)} ${arg.description}${defaultLabel}`);
}
console.log('');
Expand Down
3 changes: 2 additions & 1 deletion src/components/Attachments/AttachmentView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import type {ColorValue} from '@styles/utils/types';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import SafeString from '@src/utils/SafeString';
import AttachmentViewImage from './AttachmentViewImage';
import AttachmentViewPdf from './AttachmentViewPdf';
import AttachmentViewVideo from './AttachmentViewVideo';
Expand Down Expand Up @@ -313,7 +314,7 @@ function AttachmentView({
</>
);
}
imageSource = previewSource?.toString() ?? imageSource;
imageSource = SafeString(previewSource) || imageSource;
}

return (
Expand Down
3 changes: 2 additions & 1 deletion src/components/TextPicker/TextSelectorModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {getFieldRequiredErrors} from '@libs/ValidationUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import SafeString from '@src/utils/SafeString';
import type {TextSelectorModalProps} from './types';

function TextSelectorModal({
Expand Down Expand Up @@ -148,7 +149,7 @@ function TextSelectorModal({
ref={inputCallbackRef}
InputComponent={TextInput}
value={currentValue}
onValueChange={(changedValue) => setValue(changedValue.toString())}
onValueChange={(changedValue) => setValue(SafeString(changedValue))}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
inputID={rest.inputID}
Expand Down
5 changes: 3 additions & 2 deletions src/libs/Console/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import CONFIG from '@src/CONFIG';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Log} from '@src/types/onyx';
import SafeString from '@src/utils/SafeString';

let shouldStoreLogs = false;

Expand Down Expand Up @@ -51,7 +52,7 @@ function logMessage(args: unknown[]) {
}
}

return String(arg);
return SafeString(arg);
})
.join(' ');
const newLog = {time: new Date(), level: CONST.DEBUG_CONSOLE.LEVELS.INFO, message, extraData: ''};
Expand Down Expand Up @@ -107,7 +108,7 @@ function createLog(text: string) {
if (result !== undefined) {
return [
{time, level: CONST.DEBUG_CONSOLE.LEVELS.INFO, message: `> ${text}`, extraData: ''},
{time, level: CONST.DEBUG_CONSOLE.LEVELS.RESULT, message: String(result), extraData: ''},
{time, level: CONST.DEBUG_CONSOLE.LEVELS.RESULT, message: SafeString(result), extraData: ''},
];
}
return [{time, level: CONST.DEBUG_CONSOLE.LEVELS.INFO, message: `> ${text}`, extraData: ''}];
Expand Down
5 changes: 3 additions & 2 deletions src/libs/DebugUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {TranslationPaths} from '@src/languages/types';
import type {Beta, Report, ReportAction, ReportActions, ReportNameValuePairs, Transaction, TransactionViolation} from '@src/types/onyx';
import type {Errors} from '@src/types/onyx/OnyxCommon';
import type {Comment} from '@src/types/onyx/Transaction';
import SafeString from '@src/utils/SafeString';
import {getLinkedTransactionID} from './ReportActionsUtils';
import {getReasonAndReportActionThatRequiresAttention, reasonForReportToBeInOptionList} from './ReportUtils';
import SidebarUtils from './SidebarUtils';
Expand Down Expand Up @@ -108,7 +109,7 @@ function onyxDataToString(data: OnyxEntry<unknown>) {
return stringifyJSON(data as Record<string, unknown>);
}

return String(data);
return SafeString(data);
}

type OnyxDataType = 'number' | 'object' | 'string' | 'boolean' | 'undefined';
Expand Down Expand Up @@ -166,7 +167,7 @@ function compareStringWithOnyxData(text: string, data: OnyxEntry<unknown>) {
return text === stringifyJSON(data as Record<string, unknown>);
}

return text === String(data);
return text === SafeString(data);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/libs/IOUUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ROUTES from '@src/ROUTES';
import type {OnyxInputOrEntry, PersonalDetails, Policy, Report} from '@src/types/onyx';
import type {Attendee} from '@src/types/onyx/IOU';
import type {SearchPolicy} from '@src/types/onyx/SearchResults';
import SafeString from '@src/utils/SafeString';
import type {IOURequestType} from './actions/IOU';
import {getCurrencyUnit} from './CurrencyUtils';
import Navigation from './Navigation/Navigation';
Expand Down Expand Up @@ -217,7 +218,7 @@ function formatCurrentUserToAttendee(currentUser?: PersonalDetails, reportID?: s
email: currentUser?.login ?? '',
login: currentUser?.login ?? '',
displayName: currentUser.displayName ?? '',
avatarUrl: currentUser.avatar?.toString() ?? '',
avatarUrl: SafeString(currentUser.avatar),
accountID: currentUser.accountID,
text: currentUser.login,
selected: true,
Expand Down
9 changes: 5 additions & 4 deletions src/libs/MergeTransactionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {TranslationPaths} from '@src/languages/types';
import type {MergeTransaction, Transaction} from '@src/types/onyx';
import type {Attendee} from '@src/types/onyx/IOU';
import type {Receipt} from '@src/types/onyx/Transaction';
import SafeString from '@src/utils/SafeString';
import {convertToDisplayString} from './CurrencyUtils';
import getReceiptFilenameFromTransaction from './getReceiptFilenameFromTransaction';
import Parser from './Parser';
Expand Down Expand Up @@ -365,19 +366,19 @@ function getDisplayValue(field: MergeFieldKey, transaction: Transaction, transla
return convertToDisplayString(Number(fieldValue), getCurrency(transaction));
}
if (field === 'description') {
return StringUtils.lineBreaksToSpaces(Parser.htmlToText(fieldValue.toString()));
return StringUtils.lineBreaksToSpaces(Parser.htmlToText(SafeString(fieldValue)));
}
if (field === 'tag') {
return getCommaSeparatedTagNameWithSanitizedColons(fieldValue.toString());
return getCommaSeparatedTagNameWithSanitizedColons(SafeString(fieldValue));
}
if (field === 'reportID') {
return fieldValue === CONST.REPORT.UNREPORTED_REPORT_ID ? translate('common.none') : getReportName(getReportOrDraftReport(fieldValue.toString()));
return fieldValue === CONST.REPORT.UNREPORTED_REPORT_ID ? translate('common.none') : getReportName(getReportOrDraftReport(SafeString(fieldValue)));
}
if (field === 'attendees') {
return Array.isArray(fieldValue) ? getAttendeesListDisplayString(fieldValue) : '';
}

return String(fieldValue);
return SafeString(fieldValue);
}
/**
* Build merge fields data array from conflict fields for UI display
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {getTextFromHtml} from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import playSound, {SOUNDS} from '@libs/Sound';
import type {Report, ReportAction} from '@src/types/onyx';
import SafeString from '@src/utils/SafeString';
import focusApp from './focusApp';
import type {LocalNotificationClickHandler, LocalNotificationData, LocalNotificationModifiedExpensePushParams} from './types';

Expand Down Expand Up @@ -64,7 +65,7 @@ function push(
const notificationID = Str.guid();
notificationCache[notificationID] = new Notification(title, {
body,
icon: String(icon),
icon: SafeString(icon),
data,
silent: true,
tag,
Expand Down
3 changes: 2 additions & 1 deletion src/libs/RequestThrottle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import CONST from '@src/CONST';
import SafeString from '@src/utils/SafeString';
import {WRITE_COMMANDS} from './API/types';
import Log from './Log';
import type {RequestError} from './Network/SequentialQueue';
Expand All @@ -21,7 +22,7 @@ class RequestThrottle {
this.requestWaitTime = 0;
this.requestRetryCount = 0;
if (this.timeoutID) {
Log.info(`[RequestThrottle - ${this.name}] clearing timeoutID: ${String(this.timeoutID)}`);
Log.info(`[RequestThrottle - ${this.name}] clearing timeoutID: ${SafeString(this.timeoutID)}`);
clearTimeout(this.timeoutID);
this.timeoutID = undefined;
}
Expand Down
3 changes: 2 additions & 1 deletion src/libs/TransactionUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import type {
WaypointCollection,
} from '@src/types/onyx/Transaction';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import SafeString from '@src/utils/SafeString';
import getDistanceInMeters from './getDistanceInMeters';

type TransactionParams = {
Expand Down Expand Up @@ -1780,7 +1781,7 @@ function compareDuplicateTransactionFields(
// eslint-disable-next-line @typescript-eslint/no-deprecated
const policy = getPolicy(report?.policyID);

const areAllFieldsEqualForKey = areAllFieldsEqual(transactions, (item) => keys.map((key) => item?.[key]).join('|'));
const areAllFieldsEqualForKey = areAllFieldsEqual(transactions, (item) => keys.map((key) => SafeString(item?.[key])).join('|'));
if (fieldName === 'description') {
const allCommentsAreEqual = areAllCommentsEqual(transactions, firstTransaction);
const allCommentsAreEmpty = isFirstTransactionCommentEmptyObject && transactions.every((item) => getDescription(item) === '');
Expand Down
3 changes: 2 additions & 1 deletion src/libs/TripReservationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {Reservation, ReservationTimeDetails, ReservationType} from '@src/ty
import type Transaction from '@src/types/onyx/Transaction';
import type {AirPnr, CarPnr, HotelPnr, Pnr, PnrData, PnrTraveler, RailPnr, TripData} from '@src/types/onyx/TripData';
import type IconAsset from '@src/types/utils/IconAsset';
import SafeString from '@src/utils/SafeString';
import {getMoneyRequestSpendBreakdown} from './ReportUtils';

function getTripReservationIcon(reservationType?: ReservationType): IconAsset {
Expand Down Expand Up @@ -82,7 +83,7 @@ function parseDurationToSeconds(duration: string): number {
function getSeatByLegAndFlight(travelerInfo: ArrayValues<AirPnr['travelerInfos']>, legIdx: number, flightIdx: number): string | undefined {
const seats = travelerInfo.booking?.seats?.filter((seat) => seat.legIdx === legIdx && seat.flightIdx === flightIdx);
if (seats && seats.length > 0) {
return seats.join(', ');
return seats.map(SafeString).join(', ');
}
return '';
}
Expand Down
3 changes: 2 additions & 1 deletion src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import type {PaymentInformation} from '@src/types/onyx/LastPaymentMethod';
import type {ConnectionName} from '@src/types/onyx/Policy';
import type {SearchPolicy, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults';
import type Nullable from '@src/types/utils/Nullable';
import SafeString from '@src/utils/SafeString';
import {setPersonalBankAccountContinueKYCOnSuccess} from './BankAccounts';
import {setOptimisticTransactionThread} from './Report';
import {saveLastSearchParams} from './ReportNavigation';
Expand Down Expand Up @@ -640,7 +641,7 @@ function exportSearchItemsToCSV({query, jsonQuery, reportIDList, transactionIDLi
if (Array.isArray(value)) {
formData.append(key, value.join(','));
} else {
formData.append(key, String(value));
formData.append(key, SafeString(value));
}
});

Expand Down
7 changes: 4 additions & 3 deletions src/pages/EnablePayments/IdologyQuestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import * as BankAccounts from '@userActions/BankAccounts';
import {answerQuestionsForWallet} from '@userActions/BankAccounts';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {WalletAdditionalQuestionDetails} from '@src/types/onyx';
import type {Errors} from '@src/types/onyx/OnyxCommon';
import SafeString from '@src/utils/SafeString';

const MAX_SKIP = 1;
const SKIP_QUESTION_TEXT = 'Skip Question';
Expand Down Expand Up @@ -87,7 +88,7 @@ function IdologyQuestions({questions, idNumber}: IdologyQuestionsProps) {
}
}

BankAccounts.answerQuestionsForWallet(tempAnswers, idNumber);
answerQuestionsForWallet(tempAnswers, idNumber);
setUserAnswers(tempAnswers);
} else {
// Else, show next question
Expand Down Expand Up @@ -127,7 +128,7 @@ function IdologyQuestions({questions, idNumber}: IdologyQuestionsProps) {
possibleAnswers={possibleAnswers}
currentQuestionIndex={currentQuestionIndex}
onValueChange={(value) => {
chooseAnswer(String(value));
chooseAnswer(SafeString(value));
}}
onInputChange={() => {}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {OnyxEntry} from 'react-native-onyx';
import type {EnterSignerInfoForm} from '@src/types/form';
import INPUT_IDS from '@src/types/form/EnterSignerInfoForm';
import type {FileObject} from '@src/types/utils/Attachment';
import SafeString from '@src/utils/SafeString';

const signerDetailsFields = [
INPUT_IDS.SIGNER_FULL_NAME,
Expand Down Expand Up @@ -32,7 +33,7 @@ function getSignerDetailsAndSignerFilesForSignerInfo(enterSignerInfoFormDraft: O

if (fieldName === INPUT_IDS.SIGNER_STREET || fieldName === INPUT_IDS.SIGNER_CITY || fieldName === INPUT_IDS.SIGNER_STATE || fieldName === INPUT_IDS.SIGNER_ZIP_CODE) {
signerDetails[INPUT_IDS.SIGNER_COMPLETE_RESIDENTIAL_ADDRESS] = signerDetails[INPUT_IDS.SIGNER_COMPLETE_RESIDENTIAL_ADDRESS]
? `${String(signerDetails[INPUT_IDS.SIGNER_COMPLETE_RESIDENTIAL_ADDRESS])}, ${String(enterSignerInfoFormDraft?.[fieldName])}`
? `${SafeString(signerDetails[INPUT_IDS.SIGNER_COMPLETE_RESIDENTIAL_ADDRESS])}, ${String(enterSignerInfoFormDraft?.[fieldName])}`
: enterSignerInfoFormDraft?.[fieldName];
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import type {ReimbursementAccountForm} from '@src/types/form/ReimbursementAccountForm';
import INPUT_IDS from '@src/types/form/ReimbursementAccountForm';
import type {CorpayFormField} from '@src/types/onyx';
import SafeString from '@src/utils/SafeString';

const {ACCOUNT_HOLDER_COUNTRY} = INPUT_IDS.ADDITIONAL_DATA.CORPAY;
const {COUNTRY, ACCOUNT_HOLDER_NAME} = INPUT_IDS.ADDITIONAL_DATA;
Expand Down Expand Up @@ -77,7 +78,7 @@ function AccountHolderDetails({onNext, isEditing, corpayFields}: BankInfoSubStep
return;
}

if (new RegExp(rule.regEx).test(values[fieldID] ? String(values[fieldID]) : '')) {
if (new RegExp(rule.regEx).test(values[fieldID] ? SafeString(values[fieldID]) : '')) {
return;
}

Expand All @@ -93,7 +94,7 @@ function AccountHolderDetails({onNext, isEditing, corpayFields}: BankInfoSubStep
const inputs = useMemo(() => {
return accountHolderDetailsFields?.map((field) => {
if (field.valueSet !== undefined) {
return getInputForValueSet(field, String(defaultValues[field.id as keyof typeof defaultValues]), isEditing, styles);
return getInputForValueSet(field, SafeString(defaultValues[field.id as keyof typeof defaultValues]), isEditing, styles);
}

if (field.id === ACCOUNT_HOLDER_COUNTRY) {
Expand Down Expand Up @@ -127,7 +128,7 @@ function AccountHolderDetails({onNext, isEditing, corpayFields}: BankInfoSubStep
label={field.label}
aria-label={field.label}
role={CONST.ROLE.PRESENTATION}
defaultValue={String(defaultValues[field.id as keyof typeof defaultValues]) ?? ''}
defaultValue={SafeString(defaultValues[field.id as keyof typeof defaultValues])}
shouldSaveDraft={!isEditing}
limitSearchesToCountry={defaultValues.accountHolderCountry || defaultBankAccountCountry}
renamedInputKeys={{
Expand Down
Loading
Loading