@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
75 lines • 4.71 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.NO_LEAKED_CREDENTIALS = void 0;
const linter_format_1 = require("../linter-format");
const flowr_search_builder_1 = require("../../search/flowr-search-builder");
const range_1 = require("../../util/range");
const linter_tags_1 = require("../linter-tags");
const vertex_1 = require("../../dataflow/graph/vertex");
const edge_1 = require("../../dataflow/graph/edge");
const r_string_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-string");
const graph_1 = require("../../dataflow/graph/graph");
const defaultCredentialNamePattern = '(?:password|passwd|pwd|secret|api[_.]?key|api[_.]?token|access[_.]?token|auth[_.]?token|bearer[_.]?token|private[_.]?key|credential)';
const defaultCredentialValuePattern = '^(?:AKIA|AIPA|ASIA|AROA)[A-Z0-9]{16}' + // AWS access key IDs (exact 20-char format)
'|^gh[psoar]_[A-Za-z0-9_]{10,}' + // GitHub Classic/App/OAuth/Actions tokens
'|^github_pat_[A-Za-z0-9_]{10,}' + // GitHub fine-grained PATs
'|^glpat-[A-Za-z0-9_-]{20}' + // GitLab Personal Access Tokens (fixed length)
'|^xox[baprs]-[0-9A-Za-z]{10,}' + // Slack tokens (xoxb-, xoxp-, xoxa-, xoxr-, xoxs-)
'|^sk_(?:live|test)_[A-Za-z0-9]{24}' + // Stripe secret/test keys (length-anchored)
'|^rk_live_[A-Za-z0-9]{24}' + // Stripe restricted keys (length-anchored)
'|^npm_[A-Za-z0-9]{36}' + // npm access tokens (fixed length)
'|^shp(?:at|ss|pa|ca)_[A-Za-z0-9]{32}' + // Shopify Admin/Shared/Private/Custom tokens
'|^sk-ant-[A-Za-z0-9_-]{10,}' + // Anthropic API keys
'|^sk-proj-[A-Za-z0-9_-]{10,}' + // OpenAI project API keys
'|^SG\\.[A-Za-z0-9_-]{22}\\.' + // SendGrid API keys (structured format)
'|^-----BEGIN(?:\\s[A-Z]+)* PRIVATE KEY'; // PEM private keys (RSA, EC, DSA, OpenSSH, etc.)
exports.NO_LEAKED_CREDENTIALS = {
createSearch: () => flowr_search_builder_1.Q.all().filter(vertex_1.VertexType.VariableDefinition),
processSearchResult: async (elements, config, data) => {
const namePattern = new RegExp(config.credentialNamePattern, 'i');
const valuePattern = new RegExp(config.credentialValuePattern);
const normalize = await data.normalize();
const dfg = (await data.dataflow()).graph;
let totalChecked = 0;
const results = elements.getElements().flatMap(element => {
totalChecked++;
const name = element.node.lexeme ?? '';
const vertex = dfg.getVertex(element.node.info.id);
if (!vertex_1.VariableDefinitionVertex.is(vertex)) {
return [];
}
const nameMatches = namePattern.test(name);
for (const [targetId, edge] of dfg.outgoingEdges(element.node.info.id) ?? graph_1.NoEdges) {
if (!edge_1.DfEdge.includesType(edge, edge_1.EdgeType.DefinedBy)) {
continue;
}
const targetNode = normalize.idMap.get(targetId);
if (targetNode && r_string_1.RString.is(targetNode) && (nameMatches || valuePattern.test(targetNode.content.str))) {
return [{
certainty: linter_format_1.LintingResultCertainty.Uncertain,
involvedId: element.node.info.id,
variableName: name,
loc: range_1.SourceLocation.fromNode(element.node) ?? range_1.SourceLocation.invalid()
}];
}
}
return [];
});
return { results, '.meta': { totalChecked } };
},
prettyPrint: {
[linter_format_1.LintingPrettyPrintContext.Query]: (result) => `Possible hardcoded credential in \`${result.variableName}\` at ${range_1.SourceLocation.format(result.loc)}`,
[linter_format_1.LintingPrettyPrintContext.Full]: (result) => `Variable \`${result.variableName}\` at ${range_1.SourceLocation.format(result.loc)} appears to contain a hardcoded credential`
},
info: {
name: 'No Leaked Credentials',
description: 'Detects hardcoded credentials assigned to variables whose names suggest they hold passwords, tokens, or API keys, or whose values match known credential formats (AWS, GitHub, Slack, Stripe, SSH).',
tags: [linter_tags_1.LintingRuleTag.Security, linter_tags_1.LintingRuleTag.Experimental, linter_tags_1.LintingRuleTag.Smell],
certainty: linter_format_1.LintingRuleCertainty.BestEffort,
defaultConfig: {
credentialNamePattern: defaultCredentialNamePattern,
credentialValuePattern: defaultCredentialValuePattern
}
}
};
//# sourceMappingURL=no-leaked-credentials.js.map