@samiyev/guardian
Version:
Research-backed code quality guardian for AI-assisted development. Detects hardcodes, secrets, circular deps, framework leaks, entity exposure, and 9 architecture violations. Enforces Clean Architecture/DDD principles. Works with GitHub Copilot, Cursor, W
377 lines • 16.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HardcodedValue = void 0;
const ValueObject_1 = require("./ValueObject");
const rules_1 = require("../../shared/constants/rules");
const Suggestions_1 = require("../constants/Suggestions");
/**
* Represents a hardcoded value found in source code
*/
class HardcodedValue extends ValueObject_1.ValueObject {
constructor(props) {
super(props);
}
static create(value, type, line, column, context, valueType, duplicateLocations, withinFileUsageCount) {
return new HardcodedValue({
value,
type,
valueType,
line,
column,
context,
duplicateLocations,
withinFileUsageCount,
});
}
get value() {
return this.props.value;
}
get type() {
return this.props.type;
}
get line() {
return this.props.line;
}
get column() {
return this.props.column;
}
get context() {
return this.props.context;
}
get valueType() {
return this.props.valueType;
}
get duplicateLocations() {
return this.props.duplicateLocations;
}
get withinFileUsageCount() {
return this.props.withinFileUsageCount;
}
hasDuplicates() {
return (this.props.duplicateLocations !== undefined && this.props.duplicateLocations.length > 0);
}
isAlmostConstant() {
return this.props.withinFileUsageCount !== undefined && this.props.withinFileUsageCount >= 2;
}
isMagicNumber() {
return this.props.type === rules_1.HARDCODE_TYPES.MAGIC_NUMBER;
}
isMagicString() {
return this.props.type === rules_1.HARDCODE_TYPES.MAGIC_STRING;
}
suggestConstantName() {
if (this.isMagicNumber()) {
return this.suggestNumberConstantName();
}
if (this.isMagicString()) {
return this.suggestStringConstantName();
}
return Suggestions_1.CONSTANT_NAMES.UNKNOWN_CONSTANT;
}
suggestNumberConstantName() {
const value = this.props.value;
const context = this.props.context.toLowerCase();
if (context.includes(Suggestions_1.SUGGESTION_KEYWORDS.TIMEOUT)) {
return Suggestions_1.CONSTANT_NAMES.TIMEOUT_MS;
}
if (context.includes(Suggestions_1.SUGGESTION_KEYWORDS.RETRY) ||
context.includes(Suggestions_1.SUGGESTION_KEYWORDS.ATTEMPT)) {
return Suggestions_1.CONSTANT_NAMES.MAX_RETRIES;
}
if (context.includes(Suggestions_1.SUGGESTION_KEYWORDS.LIMIT) ||
context.includes(Suggestions_1.SUGGESTION_KEYWORDS.MAX)) {
return Suggestions_1.CONSTANT_NAMES.MAX_LIMIT;
}
if (context.includes(Suggestions_1.SUGGESTION_KEYWORDS.PORT)) {
return Suggestions_1.CONSTANT_NAMES.DEFAULT_PORT;
}
if (context.includes(Suggestions_1.SUGGESTION_KEYWORDS.DELAY)) {
return Suggestions_1.CONSTANT_NAMES.DELAY_MS;
}
return `${Suggestions_1.CONSTANT_NAMES.MAGIC_NUMBER}_${String(value)}`;
}
// eslint-disable-next-line complexity, max-lines-per-function
suggestStringConstantName() {
const value = String(this.props.value);
const context = this.props.context.toLowerCase();
const valueType = this.props.valueType;
if (valueType === "email") {
if (context.includes(Suggestions_1.EMAIL_CONTEXT_KEYWORDS.ADMIN)) {
return Suggestions_1.CONSTANT_NAMES.ADMIN_EMAIL;
}
if (context.includes(Suggestions_1.EMAIL_CONTEXT_KEYWORDS.SUPPORT)) {
return Suggestions_1.CONSTANT_NAMES.SUPPORT_EMAIL;
}
if (context.includes(Suggestions_1.EMAIL_CONTEXT_KEYWORDS.NOREPLY) ||
context.includes(Suggestions_1.EMAIL_CONTEXT_KEYWORDS.NO_REPLY)) {
return Suggestions_1.CONSTANT_NAMES.NOREPLY_EMAIL;
}
return Suggestions_1.CONSTANT_NAMES.DEFAULT_EMAIL;
}
if (valueType === "api_key") {
if (context.includes(Suggestions_1.API_KEY_CONTEXT_KEYWORDS.SECRET)) {
return Suggestions_1.CONSTANT_NAMES.API_SECRET_KEY;
}
if (context.includes(Suggestions_1.API_KEY_CONTEXT_KEYWORDS.PUBLIC)) {
return Suggestions_1.CONSTANT_NAMES.API_PUBLIC_KEY;
}
return Suggestions_1.CONSTANT_NAMES.API_KEY;
}
if (valueType === "url") {
if (context.includes(Suggestions_1.URL_CONTEXT_KEYWORDS.API)) {
return Suggestions_1.CONSTANT_NAMES.API_BASE_URL;
}
if (context.includes(Suggestions_1.URL_CONTEXT_KEYWORDS.DATABASE) ||
context.includes(Suggestions_1.URL_CONTEXT_KEYWORDS.DB)) {
return Suggestions_1.CONSTANT_NAMES.DATABASE_URL;
}
if (context.includes(Suggestions_1.URL_CONTEXT_KEYWORDS.MONGO)) {
return Suggestions_1.CONSTANT_NAMES.MONGODB_CONNECTION_STRING;
}
if (context.includes(Suggestions_1.URL_CONTEXT_KEYWORDS.POSTGRES) ||
context.includes(Suggestions_1.URL_CONTEXT_KEYWORDS.PG)) {
return Suggestions_1.CONSTANT_NAMES.POSTGRES_URL;
}
return Suggestions_1.CONSTANT_NAMES.BASE_URL;
}
if (valueType === "ip_address") {
if (context.includes(Suggestions_1.IP_CONTEXT_KEYWORDS.SERVER)) {
return Suggestions_1.CONSTANT_NAMES.SERVER_IP;
}
if (context.includes(Suggestions_1.URL_CONTEXT_KEYWORDS.DATABASE) ||
context.includes(Suggestions_1.URL_CONTEXT_KEYWORDS.DB)) {
return Suggestions_1.CONSTANT_NAMES.DATABASE_HOST;
}
if (context.includes(Suggestions_1.IP_CONTEXT_KEYWORDS.REDIS)) {
return Suggestions_1.CONSTANT_NAMES.REDIS_HOST;
}
return Suggestions_1.CONSTANT_NAMES.HOST_IP;
}
if (valueType === "file_path") {
if (context.includes(Suggestions_1.FILE_PATH_CONTEXT_KEYWORDS.LOG)) {
return Suggestions_1.CONSTANT_NAMES.LOG_FILE_PATH;
}
if (context.includes(Suggestions_1.SUGGESTION_KEYWORDS.CONFIG)) {
return Suggestions_1.CONSTANT_NAMES.CONFIG_FILE_PATH;
}
if (context.includes(Suggestions_1.FILE_PATH_CONTEXT_KEYWORDS.DATA)) {
return Suggestions_1.CONSTANT_NAMES.DATA_DIR_PATH;
}
if (context.includes(Suggestions_1.FILE_PATH_CONTEXT_KEYWORDS.TEMP)) {
return Suggestions_1.CONSTANT_NAMES.TEMP_DIR_PATH;
}
return Suggestions_1.CONSTANT_NAMES.FILE_PATH;
}
if (valueType === "date") {
if (context.includes(Suggestions_1.DATE_CONTEXT_KEYWORDS.DEADLINE)) {
return Suggestions_1.CONSTANT_NAMES.DEADLINE;
}
if (context.includes(Suggestions_1.DATE_CONTEXT_KEYWORDS.START)) {
return Suggestions_1.CONSTANT_NAMES.START_DATE;
}
if (context.includes(Suggestions_1.DATE_CONTEXT_KEYWORDS.END)) {
return Suggestions_1.CONSTANT_NAMES.END_DATE;
}
if (context.includes(Suggestions_1.DATE_CONTEXT_KEYWORDS.EXPIR)) {
return Suggestions_1.CONSTANT_NAMES.EXPIRATION_DATE;
}
return Suggestions_1.CONSTANT_NAMES.DEFAULT_DATE;
}
if (valueType === "uuid") {
if (context.includes(Suggestions_1.UUID_CONTEXT_KEYWORDS.ID) ||
context.includes(Suggestions_1.UUID_CONTEXT_KEYWORDS.IDENTIFIER)) {
return Suggestions_1.CONSTANT_NAMES.DEFAULT_ID;
}
if (context.includes(Suggestions_1.UUID_CONTEXT_KEYWORDS.REQUEST)) {
return Suggestions_1.CONSTANT_NAMES.REQUEST_ID;
}
if (context.includes(Suggestions_1.UUID_CONTEXT_KEYWORDS.SESSION)) {
return Suggestions_1.CONSTANT_NAMES.SESSION_ID;
}
return Suggestions_1.CONSTANT_NAMES.UUID_CONSTANT;
}
if (valueType === "version") {
if (context.includes(Suggestions_1.URL_CONTEXT_KEYWORDS.API)) {
return Suggestions_1.CONSTANT_NAMES.API_VERSION;
}
if (context.includes(Suggestions_1.VERSION_CONTEXT_KEYWORDS.APP)) {
return Suggestions_1.CONSTANT_NAMES.APP_VERSION;
}
return Suggestions_1.CONSTANT_NAMES.VERSION;
}
if (valueType === "color") {
if (context.includes(Suggestions_1.COLOR_CONTEXT_KEYWORDS.PRIMARY)) {
return Suggestions_1.CONSTANT_NAMES.PRIMARY_COLOR;
}
if (context.includes(Suggestions_1.COLOR_CONTEXT_KEYWORDS.SECONDARY)) {
return Suggestions_1.CONSTANT_NAMES.SECONDARY_COLOR;
}
if (context.includes(Suggestions_1.COLOR_CONTEXT_KEYWORDS.BACKGROUND)) {
return Suggestions_1.CONSTANT_NAMES.BACKGROUND_COLOR;
}
return Suggestions_1.CONSTANT_NAMES.COLOR_CONSTANT;
}
if (valueType === "mac_address") {
return Suggestions_1.CONSTANT_NAMES.MAC_ADDRESS;
}
if (valueType === "base64") {
if (context.includes(Suggestions_1.BASE64_CONTEXT_KEYWORDS.TOKEN)) {
return Suggestions_1.CONSTANT_NAMES.ENCODED_TOKEN;
}
if (context.includes(Suggestions_1.BASE64_CONTEXT_KEYWORDS.KEY)) {
return Suggestions_1.CONSTANT_NAMES.ENCODED_KEY;
}
return Suggestions_1.CONSTANT_NAMES.BASE64_VALUE;
}
if (valueType === "config") {
if (context.includes(Suggestions_1.CONFIG_CONTEXT_KEYWORDS.ENDPOINT)) {
return Suggestions_1.CONSTANT_NAMES.API_ENDPOINT;
}
if (context.includes(Suggestions_1.CONFIG_CONTEXT_KEYWORDS.ROUTE)) {
return Suggestions_1.CONSTANT_NAMES.ROUTE_PATH;
}
if (context.includes(Suggestions_1.CONFIG_CONTEXT_KEYWORDS.CONNECTION)) {
return Suggestions_1.CONSTANT_NAMES.CONNECTION_STRING;
}
return Suggestions_1.CONSTANT_NAMES.CONFIG_VALUE;
}
if (value.includes(Suggestions_1.SUGGESTION_KEYWORDS.HTTP)) {
return Suggestions_1.CONSTANT_NAMES.API_BASE_URL;
}
if (value.includes(".") && !value.includes(" ")) {
if (value.includes("/")) {
return Suggestions_1.CONSTANT_NAMES.DEFAULT_PATH;
}
return Suggestions_1.CONSTANT_NAMES.DEFAULT_DOMAIN;
}
if (context.includes(Suggestions_1.SUGGESTION_KEYWORDS.ERROR) ||
context.includes(Suggestions_1.SUGGESTION_KEYWORDS.MESSAGE)) {
return Suggestions_1.CONSTANT_NAMES.ERROR_MESSAGE;
}
if (context.includes(Suggestions_1.SUGGESTION_KEYWORDS.DEFAULT)) {
return Suggestions_1.CONSTANT_NAMES.DEFAULT_VALUE;
}
return Suggestions_1.CONSTANT_NAMES.MAGIC_STRING;
}
suggestLocation(currentLayer) {
if (!currentLayer) {
return Suggestions_1.LOCATIONS.SHARED_CONSTANTS;
}
const context = this.props.context.toLowerCase();
const valueType = this.props.valueType;
if (valueType === "api_key" || valueType === "url" || valueType === "ip_address") {
return Suggestions_1.LOCATIONS.CONFIG_ENVIRONMENT;
}
if (valueType === "email") {
return Suggestions_1.LOCATIONS.CONFIG_CONTACTS;
}
if (valueType === "file_path") {
return Suggestions_1.LOCATIONS.CONFIG_PATHS;
}
if (valueType === "date") {
return Suggestions_1.LOCATIONS.CONFIG_DATES;
}
if (context.includes(Suggestions_1.SUGGESTION_KEYWORDS.ENTITY) ||
context.includes(Suggestions_1.SUGGESTION_KEYWORDS.AGGREGATE) ||
context.includes(Suggestions_1.SUGGESTION_KEYWORDS.DOMAIN)) {
return currentLayer ? `${currentLayer}/constants` : Suggestions_1.LOCATIONS.DOMAIN_CONSTANTS;
}
if (context.includes(Suggestions_1.SUGGESTION_KEYWORDS.CONFIG) ||
context.includes(Suggestions_1.SUGGESTION_KEYWORDS.ENV)) {
return Suggestions_1.LOCATIONS.INFRASTRUCTURE_CONFIG;
}
return Suggestions_1.LOCATIONS.SHARED_CONSTANTS;
}
getDetailedSuggestion(currentLayer) {
const constantName = this.suggestConstantName();
const location = this.suggestLocation(currentLayer);
const valueTypeLabel = this.valueType ? ` (${this.valueType})` : "";
let suggestion = `Extract${valueTypeLabel} to constant ${constantName} in ${location}`;
if (this.isAlmostConstant() && this.withinFileUsageCount) {
suggestion += `. This value appears ${String(this.withinFileUsageCount)} times in this file`;
}
if (this.hasDuplicates() && this.duplicateLocations) {
const count = this.duplicateLocations.length;
const fileList = this.duplicateLocations
.slice(0, 3)
.map((loc) => `${loc.file}:${String(loc.line)}`)
.join(", ");
const more = count > 3 ? ` and ${String(count - 3)} more` : "";
suggestion += `. Also duplicated in ${String(count)} other file(s): ${fileList}${more}`;
}
return suggestion;
}
/**
* Analyzes variable name and context to determine importance
*/
getImportance() {
const context = this.props.context.toLowerCase();
const valueType = this.props.valueType;
if (valueType === "api_key") {
return "critical";
}
const criticalKeywords = [
...rules_1.DETECTION_PATTERNS.SENSITIVE_KEYWORDS,
...rules_1.DETECTION_PATTERNS.BUSINESS_KEYWORDS,
"key",
"age",
];
if (criticalKeywords.some((keyword) => context.includes(keyword))) {
return "critical";
}
const highKeywords = [...rules_1.DETECTION_PATTERNS.TECHNICAL_KEYWORDS, "db", "api"];
if (highKeywords.some((keyword) => context.includes(keyword))) {
return "high";
}
if (valueType === "url" || valueType === "ip_address" || valueType === "email") {
return "high";
}
const mediumKeywords = rules_1.DETECTION_PATTERNS.MEDIUM_KEYWORDS;
if (mediumKeywords.some((keyword) => context.includes(keyword))) {
return "medium";
}
const lowKeywords = rules_1.DETECTION_PATTERNS.UI_KEYWORDS;
if (lowKeywords.some((keyword) => context.includes(keyword))) {
return "low";
}
return "medium";
}
/**
* Checks if this violation should be skipped based on layer strictness
*
* Different layers have different tolerance levels:
* - domain: strictest (no hardcoded values allowed)
* - application: strict (only low importance allowed)
* - infrastructure: moderate (low and some medium allowed)
* - cli: lenient (UI constants allowed)
*/
shouldSkip(layer) {
if (!layer) {
return false;
}
const importance = this.getImportance();
if (layer === "domain") {
return false;
}
if (layer === "application") {
return false;
}
if (layer === "infrastructure") {
return importance === "low" && this.isUIConstant();
}
if (layer === "cli") {
return importance === "low" && this.isUIConstant();
}
return false;
}
/**
* Checks if this value is a UI-related constant
*/
isUIConstant() {
const context = this.props.context.toLowerCase();
const uiKeywords = rules_1.DETECTION_PATTERNS.UI_KEYWORDS;
return uiKeywords.some((keyword) => context.includes(keyword));
}
}
exports.HardcodedValue = HardcodedValue;
//# sourceMappingURL=HardcodedValue.js.map