@turbot/guardrails-mcp
Version:
MCP server for interacting with Turbot Guardrails.
318 lines (317 loc) • 11.2 kB
JavaScript
import _ from "lodash";
import { dump as yamlDump } from "js-yaml";
import { formatJson, formatJsonPretty } from './jsonFormatter.mjs';
// Helper functions
const formatPadString = (pad = 2) => {
if (_.isInteger(pad)) {
return " ".repeat(pad);
}
if (_.isNil(pad)) {
return " ";
}
return pad.toString();
};
function getIndentSize(indent) {
if (typeof indent === 'number') {
return indent;
}
if (typeof indent === 'string') {
const parsed = parseInt(indent, 10);
if (!isNaN(parsed)) {
return parsed;
}
}
return 2; // default indentation
}
const multilinePad = (multilineStr, pad = 0) => {
if (!pad) {
return multilineStr;
}
return multilineStr.replace(/^/gm, formatPadString(pad));
};
const padLeft = (value, length) => value.toString().padStart(length, "0");
const slackSafeString = (str) => {
if (!str || typeof str !== "string") {
return str;
}
try {
let escapedStr = str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
if (escapedStr.includes("_") || escapedStr.includes("*") || escapedStr.includes("~")) {
escapedStr = `\`${escapedStr}\``;
}
if (escapedStr.includes("```")) {
escapedStr = escapedStr.replace(/```/g, "<triple-back-tick>");
}
return escapedStr;
}
catch (e) {
return JSON.stringify({ error: e.message });
}
};
// AWS Region mapping
const awsRegion5Map = {
"ap-northeast-1": "APNE1",
"ap-northeast-2": "APNE2",
"ap-south-1": "APSO1",
"ap-southeast-1": "APSE1",
"ap-southeast-2": "APSE2",
"ca-central-1": "CACE1",
"eu-central-1": "EUCE1",
"eu-north-1": "EUNO1",
"eu-west-1": "EUWE1",
"eu-west-2": "EUWE2",
"eu-west-3": "EUWE3",
"sa-east-1": "SAEA1",
"us-east-1": "USEA1",
"us-east-2": "USEA2",
"us-west-1": "USWE1",
"us-west-2": "USWE2",
};
const awsRegion3Map = {
"ap-northeast-1": "JPN",
"ap-northeast-2": "KOR",
"ap-south-1": "IND",
"ap-southeast-1": "SGP",
"ap-southeast-2": "ANS",
"ca-central-1": "CAN",
"eu-central-1": "DEU",
"eu-north-1": "SWE",
"eu-west-1": "IRL",
"eu-west-2": "GBR",
"eu-west-3": "FRA",
"sa-east-1": "BRA",
"us-east-1": "UVA",
"us-east-2": "UOH",
"us-west-1": "UCA",
"us-west-2": "UOR",
};
const region = (inRegion) => {
switch (inRegion.length) {
case 3:
return _.invert(awsRegion3Map)[inRegion];
case 5:
return _.invert(awsRegion5Map)[inRegion];
default:
// Allow availability zones to be passed in for convenience
for (const r in awsRegion3Map) {
if (inRegion.indexOf(r) === 0) {
return r;
}
}
throw new Error(`Invalid region "${inRegion}" passed to utils.region`);
}
};
const region3 = (inRegion) => {
switch (inRegion.length) {
case 3:
return inRegion;
case 5:
return region3(region(inRegion));
default:
return awsRegion3Map[region(inRegion)];
}
};
const region5 = (inRegion) => {
switch (inRegion.length) {
case 3:
return region5(region(inRegion));
case 5:
return inRegion;
default:
return awsRegion5Map[region(inRegion)];
}
};
const centrifyLinuxUidFromSid = (sid) => {
const re = /^S-1-5-\d{2}-\d+-(\d{4,10})-\d+-(\d+)$/;
const matches = sid.match(re);
if (!matches) {
return null;
}
const securityIdBase10 = matches[1];
const relativeIdBase10 = matches[2];
const securityId = parseInt(securityIdBase10);
const relativeId = parseInt(relativeIdBase10);
const securityIdBase2 = securityId.toString(2);
const relativeIdBase2 = relativeId.toString(2);
const uidBase2String = securityIdBase2.slice(-9) + relativeIdBase2.slice(0, 22).padStart(22, "0");
const uidBase2 = parseInt(uidBase2String, 2);
return parseInt(uidBase2.toString(10));
};
// Helper for transformMap filter
const checkValue = (value, values, regexMatch) => {
for (const v in values) {
if (values[v].incorrectValues) {
values[v].incorrectValues = values[v].incorrectValues.map((val) => val.toString());
values[v].incorrectValues.forEach((incorrectValue) => {
const startsWith = incorrectValue.startsWith("string:");
if (regexMatch.test(incorrectValue) && !startsWith) {
const patternMatch = regexMatch.exec(incorrectValue);
if (patternMatch) {
const regexInIncorrectValue = new RegExp(patternMatch[1], patternMatch[2]);
if (regexInIncorrectValue.test(value)) {
value = v;
}
}
}
else if (startsWith) {
const actualIncorrectValue = incorrectValue.substring(7);
if (actualIncorrectValue === value) {
value = v;
}
}
else if (incorrectValue === value) {
value = v;
}
});
}
}
return value;
};
// Export all filters
export const filters = {
json: (obj, kwargs = {}) => {
try {
const str = formatJsonPretty(obj, getIndentSize(kwargs.indent));
return multilinePad(str, kwargs.pad);
}
catch (e) {
return formatJson({ error: e.message });
}
},
ipOctet: (ipAddr, oneBasedOctetNum) => {
const ipParts = ipAddr.split(".");
return ipParts[oneBasedOctetNum - 1];
},
ipOctetBase36Dec: (decOctetNum) => {
if (decOctetNum <= 99) {
return padLeft(decOctetNum, 2);
}
const chars = "0123456789abcdefghijklmnop";
return `${chars[Math.floor(decOctetNum / 10)]}${decOctetNum % 10}`;
},
transformMap: (data, rules) => {
const sortedDataKeys = Object.keys(data).sort();
const sortedData = {};
sortedDataKeys.forEach(key => { sortedData[key] = data[key]; });
const transformMap = {};
const regexMatch = new RegExp("^/((?:\\/|[^/])*)/([dgimsuy]*)$");
for (const property in sortedData) {
let value = sortedData[property];
let foundMatchingRule = false;
if (rules[property]) {
foundMatchingRule = true;
const ruleValues = rules[property].values;
if (ruleValues) {
value = checkValue(value, ruleValues, regexMatch);
}
transformMap[property] = value;
}
else {
for (const key in rules) {
if (rules[key].incorrectKeys) {
rules[key].incorrectKeys = rules[key].incorrectKeys.map((k) => k.toString());
rules[key].incorrectKeys.forEach((incorrectKey) => {
const startsWith = incorrectKey.startsWith("string:");
if (regexMatch.test(incorrectKey) && !startsWith) {
const patternKeyMatch = regexMatch.exec(incorrectKey);
if (patternKeyMatch) {
const regexInIncorrectKey = new RegExp(patternKeyMatch[1], patternKeyMatch[2]);
if (regexInIncorrectKey.test(property)) {
foundMatchingRule = true;
}
}
}
else if (startsWith) {
const actualIncorrectKey = incorrectKey.substring(7);
if (actualIncorrectKey === property) {
foundMatchingRule = true;
}
}
else if (incorrectKey === property) {
foundMatchingRule = true;
}
});
if (foundMatchingRule) {
if (rules[key].replacementValue) {
transformMap[property] = rules[key].replacementValue;
}
else {
transformMap[property] = sortedData[property];
}
const ruleValues = rules[key].values;
if (ruleValues) {
for (const v in ruleValues) {
if (ruleValues[v].incorrectValues) {
value = checkValue(value, ruleValues, regexMatch);
}
}
}
if (transformMap.hasOwnProperty(key)) {
continue;
}
transformMap[key] = value;
break;
}
}
}
if (!foundMatchingRule) {
transformMap[property] = value;
}
}
}
return transformMap;
},
setAttribute: (dictionary, key, value) => {
dictionary[key] = value;
return dictionary;
},
padLeft,
region3,
region5,
yaml: (obj, kwargs = {}) => {
const options = { sortKeys: true, skipInvalid: true, ...kwargs };
const safeObj = _.pick(obj, Object.keys(obj));
let str = yamlDump(safeObj, options);
str = str.trim();
return multilinePad(str, kwargs.pad);
},
alphanum: (str) => str.replace(/\W/g, ""),
date: (d, method = "toISOString") => {
let workingDate;
if (+d === +d) {
workingDate = new Date(+d);
}
else {
workingDate = new Date(d);
}
switch (method) {
case "toISOString":
return workingDate.toISOString();
case "toUTCString":
return workingDate.toUTCString();
case "toLocaleString":
return workingDate.toLocaleString();
case "toString":
return workingDate.toString();
default:
return workingDate.toISOString();
}
},
isString: _.isString,
hex: (num) => num.toString(16),
slackSafeString,
pascalCase: (str) => {
if (str.length === 0) {
return "";
}
const camelCase = _.camelCase(str);
return camelCase[0].toUpperCase() + camelCase.slice(1);
},
camelCase: _.camelCase,
snakeCase: _.snakeCase,
isArray: Array.isArray,
sidToLinuxUid: centrifyLinuxUidFromSid,
};