In the business unit where you need to audit Content Builder assets for hardcoded secrets, create a Data Extension (DE) with the following specifications:
Name: Asset_SecurityAudit
External Key: Asset_SecurityAudit
| Field Name | Data Type | Length | Primary Key | Nullable | Default Value |
| AssetId | Text | 100 | No | No | Not Applicable |
| AssetName | Text | 255 | No | No | Not Applicable |
| AssetType | Text | 100 | No | No | Not Applicable |
| FoundString | Text | 100 | No | No | Not Applicable |
| AuditDate | Date | Not Applicable | No | No | Current Date |
After the DE has been created, you will need to create an SSJS activity to fetch the assets via the Content Builder REST API and log the results. The script pages through your assets, searches the raw JSON of each asset for a specific string (such as "clientsecret"), and uses WSProxy to insert the results into the Data Extension.
NOTE: Please ensure that the subdomain, clientId, clientSecret, and mid values are supplied. This process leverages the REST API, so you will need to reference an appropriate Installed Package within your account with saved_content_read scope.
<script runat="server">
Platform.Load("Core", "1.1.1");
try {
// 1. Set up your variables
var subdomain = "YOUR_SUBDOMAIN";
var clientId = "YOUR_CLIENT_ID";
var clientSecret = "YOUR_CLIENT_SECRET";
var mid = "YOUR_MID";
var searchString = "client_secret";
var targetDEKey = "Asset_SecurityAudit";
var authUrl = "https://" + subdomain + ".auth.marketingcloudapis.com/v2/token";
var payload = {
grant_type: "client_credentials",
client_id: clientId,
client_secret: clientSecret,
account_id: mid
};
// 2. Authenticate
var authReq = new Script.Util.HttpRequest(authUrl);
authReq.emptyContentHandling = 0;
authReq.retries = 2;
authReq.continueOnError = true;
authReq.contentType = "application/json";
authReq.method = "POST";
authReq.postData = Stringify(payload);
var authResp = authReq.send();
var authJson = Platform.Function.ParseJSON(String(authResp.content));
var token = authJson.access_token;
var restUrl = authJson.rest_instance_url;
if (token) {
var prox = new Script.Util.WSProxy();
var page = 1;
var pageSize = 50;
var hasMore = true;
// 3. Loop through Asset pages
while (hasMore && page <= 500) { // Safety cap at 500 pages to prevent infinite loops
var assetsUrl = restUrl + "asset/v1/content/assets?$page=" + page + "&$pagesize=" + pageSize;
var assetReq = new Script.Util.HttpRequest(assetsUrl);
assetReq.emptyContentHandling = 0;
assetReq.retries = 2;
assetReq.continueOnError = true;
assetReq.method = "GET";
assetReq.setHeader("Authorization", "Bearer " + token);
var assetResp = assetReq.send();
var assetJson = Platform.Function.ParseJSON(String(assetResp.content));
var items = assetJson.items || [];
// 4. Search and insert
for (var i = 0; i < items.length; i++) {
// Stringify the entire asset object to catch nested content, views, and HTML
var itemString = Stringify(items[i]).toLowerCase();
if (itemString.indexOf(searchString.toLowerCase()) !== -1) {
var assetId = String(items[i].id);
var assetName = items[i].name || "Unknown";
var assetType = items[i].assetType ? items[i].assetType.name : "Unknown";
var insertProps = [
{ Name: "AssetId", Value: assetId },
{ Name: "AssetName", Value: assetName },
{ Name: "AssetType", Value: assetType },
{ Name: "FoundString", Value: searchString }
];
prox.createItem("DataExtensionObject", {
CustomerKey: targetDEKey,
Properties: insertProps
});
}
}
// 5. Check if we need to fetch the next page
if (items.length < pageSize) {
hasMore = false;
} else {
page++;
}
}
}
} catch (error) {
Write("Error: " + Stringify(error));
}
</script>
Run the above SSJS activity to search all Content Builder assets and log any identified vulnerable assets within the DE Asset_SecurityAudit.
Because this script uses pagination, it will automatically cycle through your assets in batches of 50. If the SSJS activity errors with a timeout (30mins) due to an extremely large asset library, you can manually adjust the $page variable in the script to start from where the previous run timed out.
005317466

We use three kinds of cookies on our websites: required, functional, and advertising. You can choose whether functional and advertising cookies apply. Click on the different cookie categories to find out more about each category and to change the default settings.
Privacy Statement
Required cookies are necessary for basic website functionality. Some examples include: session cookies needed to transmit the website, authentication cookies, and security cookies.
Functional cookies enhance functions, performance, and services on the website. Some examples include: cookies used to analyze site traffic, cookies used for market research, and cookies used to display advertising that is not directed to a particular individual.
Advertising cookies track activity across websites in order to understand a viewer’s interests, and direct them specific marketing. Some examples include: cookies used for remarketing, or interest-based advertising.