Skip to content

sanitize strings #113

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

Merged
merged 1 commit into from
Mar 20, 2024
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
31 changes: 31 additions & 0 deletions packages/synthetics-sdk-broken-links/src/link_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,37 @@ export function shuffleAndTruncate(
return linksToFollow.slice(0, link_limit! - 1);
}

/**
* Sanitizes an object name string for safe use, ensuring compliance with
* naming restrictions.
*
* @param {string} inputString - The original object name string.
* @returns {string} The sanitized object name.
*
* **Sanitization Rules:**
* * Removes control characters ([\u007F-\u009F]).
* * Removes disallowed characters (#, [, ], *, ?, ", <, >, |, /).
* * Replaces the forbidden prefix ".well-known/acme-challenge/" with an underscore.
* * Replaces standalone occurrences of "." or ".." with an underscore.
*/
export function sanitizeObjectName(
inputString: string | null | undefined
): string {
if (!inputString || inputString === '.' || inputString === '..') return '_';

// Regular expressions for:
/*eslint no-useless-escape: "off"*/
const invalidCharactersRegex = /[\r\n\u007F-\u009F#\[\]*?:"<>|/]/g; // Control characters, special characters, path separator
const wellKnownPrefixRegex = /^\.well-known\/acme-challenge\//;

// Core sanitization:
return inputString
.replace(wellKnownPrefixRegex, '_') // Replace forbidden prefix
.replace(invalidCharactersRegex, '_') // replace invalid characters
.trim() // Clean up any leading/trailing spaces
.replace(/\s+/g, '_'); // Replace one or more spaces with underscores
}

export function getTimeLimitPromise(
startTime: string,
totalTimeoutMillis: number,
Expand Down
17 changes: 9 additions & 8 deletions packages/synthetics-sdk-broken-links/src/storage_func.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
getExecutionRegion,
resolveProjectId,
} from '@google-cloud/synthetics-sdk-api';
import { sanitizeObjectName } from './link_utils';

// External Dependencies
import { Storage, Bucket } from '@google-cloud/storage';
Expand Down Expand Up @@ -55,14 +56,14 @@ export async function getOrCreateStorageBucket(
let bucketName = '';

try {
const projectId = await resolveProjectId();
const region = await getExecutionRegion();

// if storageClient was not properly initialized OR the user chose to
// use/create the default bucket but we were not able to resolve projectId
// or cloudRegion
if (!storageClient || (!storageLocation && (!projectId || !region)))
return null;
if (!storageClient) return null;

const projectId = sanitizeObjectName(await resolveProjectId());
const region = sanitizeObjectName(await getExecutionRegion());

// if the user chose to use/create the default bucket but we were not able
// to resolve projectId or cloudRegion
if (!storageLocation && (!projectId || !region)) return null;

bucketName = storageLocation
? storageLocation.split('/')[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
require('../../test/example_html_files/integration_server.js');
const { getTestServer } = require('@google-cloud/functions-framework/testing');

describe.only('CloudFunctionV2 Running Broken Link Synthetics', async () => {
describe('CloudFunctionV2 Running Broken Link Synthetics', async () => {
const status_class_2xx = {
status_class: ResponseStatusCode_StatusClass.STATUS_CLASS_2XX,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
BrokenLinkCheckerOptions,
} from '../../src/broken_links';

describe.only('runBrokenLinks', async () => {
describe('runBrokenLinks', async () => {
const status_class_2xx: ResponseStatusCode = {
status_class: ResponseStatusCode_StatusClass.STATUS_CLASS_2XX,
};
Expand Down
31 changes: 31 additions & 0 deletions packages/synthetics-sdk-broken-links/test/unit/link_utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
createSyntheticResult,
getGenericSyntheticResult,
LinkIntermediate,
sanitizeObjectName,
shuffleAndTruncate,
} from '../../src/link_utils';
import { setDefaultOptions } from '../../src/options_func';
Expand Down Expand Up @@ -210,4 +211,34 @@ describe('GCM Synthetics Broken Links Utilies', async () => {
expect(startTime).to.be.lessThan(endTime);
expect(milliDifference).to.be.greaterThan(0);
});

describe('sanitizeObjectName', () => {
it('should remove invalid characters', () => {
const input = "test/\@#$%^&*()/_+\-=[]{};':\"\|,.<>/?\r\n\t";
const expectedOutput = "test_@_$%^&_()__+-=__{};'__\_,.______";
expect(sanitizeObjectName(input)).to.equal(expectedOutput);
});

it('should replace the forbidden prefix', () => {
const input = ".well-known/acme-challenge/test";
const expectedOutput = "_test";
expect(sanitizeObjectName(input)).to.equal(expectedOutput);
});

it('should handle standalone "." and ".."', () => {
expect(sanitizeObjectName(".")).to.equal("_");
expect(sanitizeObjectName("..")).to.equal("_");
});

it('should handle null and undefined', () => {
expect(sanitizeObjectName(null)).to.equal("_");
expect(sanitizeObjectName(undefined)).to.equal("_");
})

it('should trim leading and trailing whitespace', () => {
const input = " test name ";
const expectedOutput = "test_name";
expect(sanitizeObjectName(input)).to.equal(expectedOutput);
});
});
});