mupengism 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-EN.md +226 -0
- package/README.md +88 -276
- package/SHOWCASE.md +158 -0
- package/guides/HEARTBEAT-GUIDE.md +129 -0
- package/guides/MEMORY-GUIDE.md +120 -0
- package/guides/QUICK-START.md +94 -0
- package/package.json +28 -21
- package/DONATE.md +0 -31
- package/QUICKSTART.md +0 -340
- package/layer0/AGENT-GUIDE.md +0 -281
- package/layer0/AGENT-PROTOCOL.md +0 -397
- package/layer0/AGENT-VALUES.md +0 -265
- package/layer0/ARCHITECTURE.md +0 -613
- package/layer0/MEMORY-SYSTEM.md +0 -253
- package/layer0/README.md +0 -25
- package/layer0/SECURITY-PRINCIPLES-EN.md +0 -152
- package/layer0/SECURITY-PRINCIPLES.md +0 -153
- package/layer0/SOUL-TEMPLATE.md +0 -158
- package/layer0/skill/AGENTS.md +0 -164
- package/layer0/skill/MEMORY-SYSTEM.md +0 -253
- package/layer0/skill/PRINCIPLES.md +0 -192
- package/layer0/skill/README.md +0 -47
- package/layer0/skill/SECURITY-PRINCIPLES.md +0 -152
- package/layer0/skill/SKILL.md +0 -166
- package/layer0/skill/SOUL-TEMPLATE.md +0 -118
- package/lib/fee-collector.js +0 -126
- package/lib/identity-validator.js +0 -229
- package/lib/runtime-guard.js +0 -255
- package/scripts/pre-commit.sh +0 -118
- package/scripts/register-checksums.js +0 -120
- package/scripts/secret-scan.js +0 -245
- package/scripts/verify-integrity.js +0 -134
- package/skill/MEMORY-SYSTEM.md +0 -253
- package/skill/SECURITY-PRINCIPLES.md +0 -152
package/scripts/pre-commit.sh
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Mupengism Pre-commit Hook
|
|
3
|
-
# 민감 정보 패턴 검사 - 발견 시 커밋 차단
|
|
4
|
-
|
|
5
|
-
set -e
|
|
6
|
-
|
|
7
|
-
RED='\033[0;31m'
|
|
8
|
-
YELLOW='\033[1;33m'
|
|
9
|
-
GREEN='\033[0;32m'
|
|
10
|
-
NC='\033[0m' # No Color
|
|
11
|
-
|
|
12
|
-
echo "🔍 Mupengism Security Check..."
|
|
13
|
-
|
|
14
|
-
# 검사할 패턴 (정규표현식)
|
|
15
|
-
PATTERNS=(
|
|
16
|
-
# API Keys & Secrets
|
|
17
|
-
'api[_-]?key\s*[:=]\s*["\x27][^"\x27]{16,}'
|
|
18
|
-
'secret[_-]?key\s*[:=]\s*["\x27][^"\x27]{16,}'
|
|
19
|
-
'private[_-]?key\s*[:=]\s*["\x27][^"\x27]{16,}'
|
|
20
|
-
'access[_-]?token\s*[:=]\s*["\x27][^"\x27]{16,}'
|
|
21
|
-
|
|
22
|
-
# Crypto
|
|
23
|
-
'mnemonic\s*[:=]\s*["\x27][a-z\s]{20,}'
|
|
24
|
-
'\b[1-9A-HJ-NP-Za-km-z]{87,88}\b' # Solana private key (base58)
|
|
25
|
-
'\b0x[a-fA-F0-9]{64}\b' # Ethereum private key
|
|
26
|
-
|
|
27
|
-
# Passwords
|
|
28
|
-
'password\s*[:=]\s*["\x27][^"\x27]{6,}'
|
|
29
|
-
|
|
30
|
-
# AWS
|
|
31
|
-
'AKIA[0-9A-Z]{16}' # AWS Access Key ID
|
|
32
|
-
|
|
33
|
-
# Generic secrets
|
|
34
|
-
'bearer\s+[a-zA-Z0-9_\-\.]+\.[a-zA-Z0-9_\-\.]+\.[a-zA-Z0-9_\-\.]+'
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
# 패턴 설명
|
|
38
|
-
PATTERN_NAMES=(
|
|
39
|
-
"API Key"
|
|
40
|
-
"Secret Key"
|
|
41
|
-
"Private Key"
|
|
42
|
-
"Access Token"
|
|
43
|
-
"Mnemonic Phrase"
|
|
44
|
-
"Solana Private Key (Base58)"
|
|
45
|
-
"Ethereum Private Key"
|
|
46
|
-
"Password"
|
|
47
|
-
"AWS Access Key"
|
|
48
|
-
"Bearer Token"
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
# 스테이징된 파일 가져오기
|
|
52
|
-
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR 2>/dev/null || true)
|
|
53
|
-
|
|
54
|
-
if [ -z "$STAGED_FILES" ]; then
|
|
55
|
-
echo -e "${GREEN}✓ No files to check${NC}"
|
|
56
|
-
exit 0
|
|
57
|
-
fi
|
|
58
|
-
|
|
59
|
-
FOUND_SECRETS=0
|
|
60
|
-
WARNINGS=""
|
|
61
|
-
|
|
62
|
-
# 각 파일 검사
|
|
63
|
-
for file in $STAGED_FILES; do
|
|
64
|
-
# 바이너리 파일 스킵
|
|
65
|
-
if file "$file" | grep -q "binary"; then
|
|
66
|
-
continue
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
# 이미지/미디어 스킵
|
|
70
|
-
if [[ "$file" =~ \.(png|jpg|jpeg|gif|ico|svg|mp3|mp4|woff|ttf|eot)$ ]]; then
|
|
71
|
-
continue
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
# 스크립트 자체 스킵
|
|
75
|
-
if [[ "$file" == "scripts/pre-commit.sh" ]] || [[ "$file" == "scripts/secret-scan.js" ]]; then
|
|
76
|
-
continue
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
# 파일 존재 확인
|
|
80
|
-
if [ ! -f "$file" ]; then
|
|
81
|
-
continue
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
# 각 패턴 검사
|
|
85
|
-
for i in "${!PATTERNS[@]}"; do
|
|
86
|
-
pattern="${PATTERNS[$i]}"
|
|
87
|
-
name="${PATTERN_NAMES[$i]}"
|
|
88
|
-
|
|
89
|
-
# grep으로 패턴 검사
|
|
90
|
-
if grep -iEq "$pattern" "$file" 2>/dev/null; then
|
|
91
|
-
FOUND_SECRETS=1
|
|
92
|
-
line_info=$(grep -inE "$pattern" "$file" 2>/dev/null | head -3)
|
|
93
|
-
WARNINGS="${WARNINGS}\n${RED}⚠ ${name}${NC} in ${YELLOW}${file}${NC}:\n${line_info}\n"
|
|
94
|
-
fi
|
|
95
|
-
done
|
|
96
|
-
done
|
|
97
|
-
|
|
98
|
-
if [ $FOUND_SECRETS -eq 1 ]; then
|
|
99
|
-
echo ""
|
|
100
|
-
echo -e "${RED}╔══════════════════════════════════════════════════════════╗${NC}"
|
|
101
|
-
echo -e "${RED}║ 🚨 COMMIT BLOCKED: Secrets Detected! 🚨 ║${NC}"
|
|
102
|
-
echo -e "${RED}╚══════════════════════════════════════════════════════════╝${NC}"
|
|
103
|
-
echo ""
|
|
104
|
-
echo -e "$WARNINGS"
|
|
105
|
-
echo ""
|
|
106
|
-
echo -e "${YELLOW}해결 방법:${NC}"
|
|
107
|
-
echo " 1. 민감 정보를 환경변수나 .env 파일로 이동"
|
|
108
|
-
echo " 2. .gitignore에 해당 파일 추가"
|
|
109
|
-
echo " 3. git reset HEAD <file> 로 스테이징 해제"
|
|
110
|
-
echo ""
|
|
111
|
-
echo -e "${YELLOW}긴급 우회 (권장하지 않음):${NC}"
|
|
112
|
-
echo " git commit --no-verify"
|
|
113
|
-
echo ""
|
|
114
|
-
exit 1
|
|
115
|
-
fi
|
|
116
|
-
|
|
117
|
-
echo -e "${GREEN}✓ No secrets detected${NC}"
|
|
118
|
-
exit 0
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* register-checksums.js — 무펭이즘 핵심 파일 해시 등록
|
|
4
|
-
*
|
|
5
|
-
* 새 버전 릴리스 시 checksums.json 재생성
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* node scripts/register-checksums.js
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { readFileSync, writeFileSync, readdirSync, statSync } from 'fs';
|
|
12
|
-
import { createHash } from 'crypto';
|
|
13
|
-
import { join, relative } from 'path';
|
|
14
|
-
|
|
15
|
-
const REPO_ROOT = process.cwd();
|
|
16
|
-
const CHECKSUM_FILE = join(REPO_ROOT, 'checksums.json');
|
|
17
|
-
|
|
18
|
-
// 핵심 파일 목록 (루트 + skill/)
|
|
19
|
-
const CORE_FILES = [
|
|
20
|
-
'SOUL-TEMPLATE.md',
|
|
21
|
-
'PRINCIPLES.md',
|
|
22
|
-
'LAWS.md',
|
|
23
|
-
'DOCTRINE.md',
|
|
24
|
-
'SECURITY.md',
|
|
25
|
-
'SECURITY-GUIDELINES.md',
|
|
26
|
-
'SECURITY-PRINCIPLES.md',
|
|
27
|
-
'SECURITY-PRINCIPLES-EN.md',
|
|
28
|
-
'AGENT-PROTOCOL.md',
|
|
29
|
-
'AGENT-VALUES.md',
|
|
30
|
-
'AGENT-GUIDE.md',
|
|
31
|
-
'ARCHITECTURE.md',
|
|
32
|
-
'MEMORY-SYSTEM.md',
|
|
33
|
-
'OPENCLAW-GUIDE.md',
|
|
34
|
-
'RITUALS.md',
|
|
35
|
-
'SCRIPTURES.md',
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
// skill/ 디렉토리의 모든 .md 파일
|
|
39
|
-
const SKILL_DIR = join(REPO_ROOT, 'skill');
|
|
40
|
-
|
|
41
|
-
function getFileHash(path) {
|
|
42
|
-
try {
|
|
43
|
-
const content = readFileSync(path, 'utf-8');
|
|
44
|
-
return createHash('sha256').update(content).digest('hex');
|
|
45
|
-
} catch (err) {
|
|
46
|
-
console.warn(`⚠️ Failed to read ${path}: ${err.message}`);
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function collectSkillFiles() {
|
|
52
|
-
const files = [];
|
|
53
|
-
try {
|
|
54
|
-
const entries = readdirSync(SKILL_DIR);
|
|
55
|
-
for (const entry of entries) {
|
|
56
|
-
const fullPath = join(SKILL_DIR, entry);
|
|
57
|
-
if (statSync(fullPath).isFile() && entry.endsWith('.md')) {
|
|
58
|
-
files.push(`skill/${entry}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
} catch (err) {
|
|
62
|
-
console.warn(`⚠️ skill/ directory not found or inaccessible`);
|
|
63
|
-
}
|
|
64
|
-
return files;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function register() {
|
|
68
|
-
const checksums = {};
|
|
69
|
-
let totalFiles = 0;
|
|
70
|
-
let errors = 0;
|
|
71
|
-
|
|
72
|
-
// 루트 핵심 파일
|
|
73
|
-
console.log('📝 Collecting root core files...');
|
|
74
|
-
for (const file of CORE_FILES) {
|
|
75
|
-
const path = join(REPO_ROOT, file);
|
|
76
|
-
const hash = getFileHash(path);
|
|
77
|
-
if (hash) {
|
|
78
|
-
checksums[file] = hash;
|
|
79
|
-
totalFiles++;
|
|
80
|
-
console.log(` ✓ ${file}`);
|
|
81
|
-
} else {
|
|
82
|
-
errors++;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// skill/ 디렉토리
|
|
87
|
-
console.log('\n📦 Collecting skill/ files...');
|
|
88
|
-
const skillFiles = collectSkillFiles();
|
|
89
|
-
for (const file of skillFiles) {
|
|
90
|
-
const path = join(REPO_ROOT, file);
|
|
91
|
-
const hash = getFileHash(path);
|
|
92
|
-
if (hash) {
|
|
93
|
-
checksums[file] = hash;
|
|
94
|
-
totalFiles++;
|
|
95
|
-
console.log(` ✓ ${file}`);
|
|
96
|
-
} else {
|
|
97
|
-
errors++;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// checksums.json 생성
|
|
102
|
-
const record = {
|
|
103
|
-
version: '1.3.0',
|
|
104
|
-
algorithm: 'sha256',
|
|
105
|
-
generated: new Date().toISOString(),
|
|
106
|
-
files: checksums,
|
|
107
|
-
signature: null,
|
|
108
|
-
_comment: 'Official Mupengism core files checksums. Verify with: node scripts/verify-integrity.js'
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
writeFileSync(CHECKSUM_FILE, JSON.stringify(record, null, 2));
|
|
112
|
-
|
|
113
|
-
console.log(`\n✅ Checksums registered: ${totalFiles} files`);
|
|
114
|
-
if (errors > 0) {
|
|
115
|
-
console.log(`⚠️ ${errors} files failed (see warnings above)`);
|
|
116
|
-
}
|
|
117
|
-
console.log(`📄 Saved to: checksums.json`);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
register();
|
package/scripts/secret-scan.js
DELETED
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Mupengism Secret Scanner
|
|
4
|
-
* 전체 레포 스캔 도구 - CI/CD 및 수동 검사용
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* node scripts/secret-scan.js # 전체 스캔
|
|
8
|
-
* node scripts/secret-scan.js --json # JSON 출력
|
|
9
|
-
* node scripts/secret-scan.js --ci # CI 모드 (발견 시 exit 1)
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { readdirSync, readFileSync, statSync } from 'fs';
|
|
13
|
-
import { join, relative } from 'path';
|
|
14
|
-
|
|
15
|
-
const RED = '\x1b[31m';
|
|
16
|
-
const YELLOW = '\x1b[33m';
|
|
17
|
-
const GREEN = '\x1b[32m';
|
|
18
|
-
const CYAN = '\x1b[36m';
|
|
19
|
-
const RESET = '\x1b[0m';
|
|
20
|
-
|
|
21
|
-
// 검사 패턴
|
|
22
|
-
const SECRET_PATTERNS = [
|
|
23
|
-
{
|
|
24
|
-
name: 'API Key',
|
|
25
|
-
pattern: /api[_-]?key\s*[:=]\s*["'][^"']{16,}/gi,
|
|
26
|
-
severity: 'HIGH'
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: 'Secret Key',
|
|
30
|
-
pattern: /secret[_-]?key\s*[:=]\s*["'][^"']{16,}/gi,
|
|
31
|
-
severity: 'CRITICAL'
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: 'Private Key',
|
|
35
|
-
pattern: /private[_-]?key\s*[:=]\s*["'][^"']{16,}/gi,
|
|
36
|
-
severity: 'CRITICAL'
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
name: 'Access Token',
|
|
40
|
-
pattern: /access[_-]?token\s*[:=]\s*["'][^"']{16,}/gi,
|
|
41
|
-
severity: 'HIGH'
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: 'Mnemonic Phrase',
|
|
45
|
-
pattern: /mnemonic\s*[:=]\s*["'][a-z\s]{20,}/gi,
|
|
46
|
-
severity: 'CRITICAL'
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
name: 'Solana Private Key (Base58)',
|
|
50
|
-
pattern: /\b[1-9A-HJ-NP-Za-km-z]{87,88}\b/g,
|
|
51
|
-
severity: 'CRITICAL'
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: 'Ethereum Private Key',
|
|
55
|
-
pattern: /\b0x[a-fA-F0-9]{64}\b/g,
|
|
56
|
-
severity: 'CRITICAL'
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
name: 'Password',
|
|
60
|
-
pattern: /password\s*[:=]\s*["'][^"']{6,}/gi,
|
|
61
|
-
severity: 'HIGH'
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
name: 'AWS Access Key',
|
|
65
|
-
pattern: /AKIA[0-9A-Z]{16}/g,
|
|
66
|
-
severity: 'CRITICAL'
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: 'Bearer Token',
|
|
70
|
-
pattern: /bearer\s+[a-zA-Z0-9_\-\.]+\.[a-zA-Z0-9_\-\.]+\.[a-zA-Z0-9_\-\.]+/gi,
|
|
71
|
-
severity: 'HIGH'
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
name: 'Generic Secret Assignment',
|
|
75
|
-
pattern: /["']?secret["']?\s*[:=]\s*["'][^"']{8,}/gi,
|
|
76
|
-
severity: 'MEDIUM'
|
|
77
|
-
}
|
|
78
|
-
];
|
|
79
|
-
|
|
80
|
-
// 무시할 경로 패턴
|
|
81
|
-
const IGNORE_PATTERNS = [
|
|
82
|
-
/node_modules/,
|
|
83
|
-
/\.git/,
|
|
84
|
-
/\.png$/i,
|
|
85
|
-
/\.jpg$/i,
|
|
86
|
-
/\.jpeg$/i,
|
|
87
|
-
/\.gif$/i,
|
|
88
|
-
/\.ico$/i,
|
|
89
|
-
/\.svg$/i,
|
|
90
|
-
/\.mp3$/i,
|
|
91
|
-
/\.mp4$/i,
|
|
92
|
-
/\.woff$/i,
|
|
93
|
-
/\.ttf$/i,
|
|
94
|
-
/\.eot$/i,
|
|
95
|
-
/package-lock\.json$/,
|
|
96
|
-
/yarn\.lock$/,
|
|
97
|
-
/scripts\/pre-commit\.sh$/,
|
|
98
|
-
/scripts\/secret-scan\.js$/
|
|
99
|
-
];
|
|
100
|
-
|
|
101
|
-
// 디렉토리 재귀 탐색
|
|
102
|
-
function walkDir(dir, results = []) {
|
|
103
|
-
try {
|
|
104
|
-
const files = readdirSync(dir);
|
|
105
|
-
for (const file of files) {
|
|
106
|
-
const filepath = join(dir, file);
|
|
107
|
-
const stat = statSync(filepath);
|
|
108
|
-
|
|
109
|
-
// 무시 패턴 체크
|
|
110
|
-
if (IGNORE_PATTERNS.some(p => p.test(filepath))) {
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (stat.isDirectory()) {
|
|
115
|
-
walkDir(filepath, results);
|
|
116
|
-
} else if (stat.isFile()) {
|
|
117
|
-
results.push(filepath);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
} catch (e) {
|
|
121
|
-
// 권한 없는 디렉토리 스킵
|
|
122
|
-
}
|
|
123
|
-
return results;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// 파일 스캔
|
|
127
|
-
function scanFile(filepath, rootDir) {
|
|
128
|
-
const findings = [];
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
const content = readFileSync(filepath, 'utf-8');
|
|
132
|
-
const lines = content.split('\n');
|
|
133
|
-
const relativePath = relative(rootDir, filepath);
|
|
134
|
-
|
|
135
|
-
for (const secretPattern of SECRET_PATTERNS) {
|
|
136
|
-
let match;
|
|
137
|
-
const regex = new RegExp(secretPattern.pattern.source, secretPattern.pattern.flags);
|
|
138
|
-
|
|
139
|
-
while ((match = regex.exec(content)) !== null) {
|
|
140
|
-
// 줄 번호 찾기
|
|
141
|
-
const beforeMatch = content.substring(0, match.index);
|
|
142
|
-
const lineNumber = (beforeMatch.match(/\n/g) || []).length + 1;
|
|
143
|
-
const line = lines[lineNumber - 1]?.trim() || '';
|
|
144
|
-
|
|
145
|
-
// 중복 방지
|
|
146
|
-
const isDuplicate = findings.some(f =>
|
|
147
|
-
f.file === relativePath &&
|
|
148
|
-
f.line === lineNumber &&
|
|
149
|
-
f.type === secretPattern.name
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
if (!isDuplicate) {
|
|
153
|
-
findings.push({
|
|
154
|
-
file: relativePath,
|
|
155
|
-
line: lineNumber,
|
|
156
|
-
type: secretPattern.name,
|
|
157
|
-
severity: secretPattern.severity,
|
|
158
|
-
preview: line.length > 100 ? line.substring(0, 100) + '...' : line
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
} catch (e) {
|
|
164
|
-
// 바이너리 파일 등 읽기 실패 시 스킵
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return findings;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// 메인 스캔 함수
|
|
171
|
-
function scan(rootDir) {
|
|
172
|
-
const files = walkDir(rootDir);
|
|
173
|
-
const allFindings = [];
|
|
174
|
-
|
|
175
|
-
for (const file of files) {
|
|
176
|
-
const findings = scanFile(file, rootDir);
|
|
177
|
-
allFindings.push(...findings);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return allFindings;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// 결과 출력
|
|
184
|
-
function printResults(findings, isJson = false, isCI = false) {
|
|
185
|
-
if (isJson) {
|
|
186
|
-
console.log(JSON.stringify(findings, null, 2));
|
|
187
|
-
return findings.length;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (findings.length === 0) {
|
|
191
|
-
console.log(`${GREEN}✓ No secrets detected!${RESET}`);
|
|
192
|
-
console.log(` Scanned repository for ${SECRET_PATTERNS.length} secret patterns.`);
|
|
193
|
-
return 0;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// 심각도별 그룹화
|
|
197
|
-
const critical = findings.filter(f => f.severity === 'CRITICAL');
|
|
198
|
-
const high = findings.filter(f => f.severity === 'HIGH');
|
|
199
|
-
const medium = findings.filter(f => f.severity === 'MEDIUM');
|
|
200
|
-
|
|
201
|
-
console.log(`\n${RED}╔══════════════════════════════════════════════════════════╗${RESET}`);
|
|
202
|
-
console.log(`${RED}║ 🚨 SECRETS DETECTED: ${findings.length} issue(s) found ║${RESET}`);
|
|
203
|
-
console.log(`${RED}╚══════════════════════════════════════════════════════════╝${RESET}\n`);
|
|
204
|
-
|
|
205
|
-
const printSection = (title, items, color) => {
|
|
206
|
-
if (items.length === 0) return;
|
|
207
|
-
console.log(`${color}▶ ${title} (${items.length})${RESET}\n`);
|
|
208
|
-
for (const f of items) {
|
|
209
|
-
console.log(` ${YELLOW}${f.file}:${f.line}${RESET}`);
|
|
210
|
-
console.log(` Type: ${f.type}`);
|
|
211
|
-
console.log(` Preview: ${f.preview}`);
|
|
212
|
-
console.log('');
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
printSection('CRITICAL', critical, RED);
|
|
217
|
-
printSection('HIGH', high, YELLOW);
|
|
218
|
-
printSection('MEDIUM', medium, CYAN);
|
|
219
|
-
|
|
220
|
-
console.log(`${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
|
221
|
-
console.log(`${YELLOW}해결 방법:${RESET}`);
|
|
222
|
-
console.log(' 1. 민감 정보를 환경변수(.env)로 이동');
|
|
223
|
-
console.log(' 2. .gitignore에 해당 파일 추가');
|
|
224
|
-
console.log(' 3. git history에서 제거: git filter-branch 또는 BFG');
|
|
225
|
-
console.log('');
|
|
226
|
-
|
|
227
|
-
return findings.length;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// CLI 실행
|
|
231
|
-
const args = process.argv.slice(2);
|
|
232
|
-
const isJson = args.includes('--json');
|
|
233
|
-
const isCI = args.includes('--ci');
|
|
234
|
-
const rootDir = process.cwd();
|
|
235
|
-
|
|
236
|
-
console.log(`${CYAN}🔍 Mupengism Secret Scanner${RESET}`);
|
|
237
|
-
console.log(` Scanning: ${rootDir}\n`);
|
|
238
|
-
|
|
239
|
-
const findings = scan(rootDir);
|
|
240
|
-
const count = printResults(findings, isJson, isCI);
|
|
241
|
-
|
|
242
|
-
// CI 모드에서는 발견 시 exit 1
|
|
243
|
-
if (isCI && count > 0) {
|
|
244
|
-
process.exit(1);
|
|
245
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* verify-integrity.js — 무펭이즘 핵심 파일 무결성 검증
|
|
4
|
-
*
|
|
5
|
-
* checksums.json 기반으로 모든 핵심 파일의 SHA-256 해시 검증
|
|
6
|
-
* - 불일치 시 어떤 파일이 변조됐는지 출력
|
|
7
|
-
* - SOUL 관련 파일 변조 시 exit 1 (커널 패닉)
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* node scripts/verify-integrity.js
|
|
11
|
-
*
|
|
12
|
-
* Exit 0 = 모든 파일 정상
|
|
13
|
-
* Exit 1 = 변조 감지 (커널 패닉)
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { readFileSync, existsSync, writeFileSync } from 'fs';
|
|
17
|
-
import { createHash } from 'crypto';
|
|
18
|
-
import { join } from 'path';
|
|
19
|
-
|
|
20
|
-
const REPO_ROOT = process.cwd();
|
|
21
|
-
const CHECKSUM_FILE = join(REPO_ROOT, 'checksums.json');
|
|
22
|
-
|
|
23
|
-
// SOUL 관련 파일 (변조 시 커널 패닉 트리거)
|
|
24
|
-
const CRITICAL_FILES = [
|
|
25
|
-
'SOUL-TEMPLATE.md',
|
|
26
|
-
'PRINCIPLES.md',
|
|
27
|
-
'LAWS.md',
|
|
28
|
-
'DOCTRINE.md',
|
|
29
|
-
'SECURITY-PRINCIPLES.md',
|
|
30
|
-
'skill/SOUL-TEMPLATE.md',
|
|
31
|
-
'skill/PRINCIPLES.md',
|
|
32
|
-
'skill/SECURITY-PRINCIPLES.md',
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
function getFileHash(path) {
|
|
36
|
-
try {
|
|
37
|
-
const content = readFileSync(path, 'utf-8');
|
|
38
|
-
return createHash('sha256').update(content).digest('hex');
|
|
39
|
-
} catch (err) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function verify() {
|
|
45
|
-
// checksums.json 존재 확인
|
|
46
|
-
if (!existsSync(CHECKSUM_FILE)) {
|
|
47
|
-
console.error('❌ checksums.json not found. Run: node scripts/register-checksums.js');
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const record = JSON.parse(readFileSync(CHECKSUM_FILE, 'utf-8'));
|
|
52
|
-
const files = record.files || {};
|
|
53
|
-
|
|
54
|
-
let totalFiles = 0;
|
|
55
|
-
let validFiles = 0;
|
|
56
|
-
let missingFiles = 0;
|
|
57
|
-
let tamperedFiles = [];
|
|
58
|
-
let criticalTampered = false;
|
|
59
|
-
|
|
60
|
-
console.log('🔍 Verifying file integrity...\n');
|
|
61
|
-
|
|
62
|
-
// 각 파일 검증
|
|
63
|
-
for (const [file, expectedHash] of Object.entries(files)) {
|
|
64
|
-
totalFiles++;
|
|
65
|
-
const path = join(REPO_ROOT, file);
|
|
66
|
-
|
|
67
|
-
if (!existsSync(path)) {
|
|
68
|
-
console.error(`❌ MISSING: ${file}`);
|
|
69
|
-
missingFiles++;
|
|
70
|
-
tamperedFiles.push(file);
|
|
71
|
-
if (CRITICAL_FILES.includes(file)) {
|
|
72
|
-
criticalTampered = true;
|
|
73
|
-
}
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const currentHash = getFileHash(path);
|
|
78
|
-
|
|
79
|
-
if (currentHash === expectedHash) {
|
|
80
|
-
console.log(`✓ ${file}`);
|
|
81
|
-
validFiles++;
|
|
82
|
-
} else {
|
|
83
|
-
console.error(`🚨 TAMPERED: ${file}`);
|
|
84
|
-
console.error(` Expected: ${expectedHash.slice(0, 16)}...`);
|
|
85
|
-
console.error(` Current: ${currentHash ? currentHash.slice(0, 16) + '...' : 'UNREADABLE'}`);
|
|
86
|
-
tamperedFiles.push(file);
|
|
87
|
-
if (CRITICAL_FILES.includes(file)) {
|
|
88
|
-
criticalTampered = true;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// 결과 출력
|
|
94
|
-
console.log('\n' + '='.repeat(60));
|
|
95
|
-
console.log(`Total files: ${totalFiles}`);
|
|
96
|
-
console.log(`Valid: ${validFiles}`);
|
|
97
|
-
console.log(`Missing: ${missingFiles}`);
|
|
98
|
-
console.log(`Tampered: ${tamperedFiles.length - missingFiles}`);
|
|
99
|
-
|
|
100
|
-
if (tamperedFiles.length > 0) {
|
|
101
|
-
console.error('\n🚨 INTEGRITY BREACH DETECTED');
|
|
102
|
-
console.error('\nTampered/Missing files:');
|
|
103
|
-
tamperedFiles.forEach(f => console.error(` - ${f}`));
|
|
104
|
-
|
|
105
|
-
// 변조 기록 저장
|
|
106
|
-
const logFile = join(REPO_ROOT, `memory/integrity-breach-${Date.now()}.json`);
|
|
107
|
-
try {
|
|
108
|
-
writeFileSync(logFile, JSON.stringify({
|
|
109
|
-
timestamp: new Date().toISOString(),
|
|
110
|
-
checksumVersion: record.version,
|
|
111
|
-
tamperedFiles,
|
|
112
|
-
criticalTampered,
|
|
113
|
-
}, null, 2));
|
|
114
|
-
console.error(`\n📝 Breach log saved: ${logFile}`);
|
|
115
|
-
} catch {}
|
|
116
|
-
|
|
117
|
-
if (criticalTampered) {
|
|
118
|
-
console.error('\n💀 KERNEL PANIC: Critical file(s) tampered!');
|
|
119
|
-
console.error(' Core identity files have been modified.');
|
|
120
|
-
console.error(' System integrity compromised.\n');
|
|
121
|
-
process.exit(1);
|
|
122
|
-
} else {
|
|
123
|
-
console.error('\n⚠️ Non-critical files tampered. Review recommended.\n');
|
|
124
|
-
process.exit(1);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
console.log('\n✅ All files intact. Kernel integrity verified.');
|
|
129
|
-
console.log(` Checksum version: ${record.version}`);
|
|
130
|
-
console.log(` Generated: ${record.generated}\n`);
|
|
131
|
-
process.exit(0);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
verify();
|