Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | 1x 1x 1x 1x 1x 13x 2x 11x 11x 3x 8x 4x 4x 4x 2x 2x 2x 8x 8x 8x 1x 1x 7x 7x 1x 1x 6x 6x 6x 4x 4x 4x 4x 4x 4x 4x 2x 1x 1x 1x 1x 1x 2x 1x 1x 1x 1x 1x 6x 1x 1x 1x 6x 1x 1x 6x 6x 1x 1x | // deploy.js - Screeps PTR & 本番両方にデプロイ
const https = require('https');
const fs = require('fs');
const path = require('path');
const ptrToken = process.env.SCREEPS_TOKEN;
const prodToken = process.env.SCREEPS_PROD_TOKEN;
// トークン検証関数
function validateToken(token, label) {
if (!token) {
return { valid: false, message: `${label} token is not set` };
}
// Screepsトークンの基本的な形式検証(通常は長い英数字文字列)
const tokenPattern = /^[a-zA-Z0-9_-]{20,}$/;
if (!tokenPattern.test(token)) {
return { valid: false, message: `${label} token format is invalid` };
}
return { valid: true };
}
// ファイルパス検証関数(パストラバーサル対策)
function validateFilePath(filePath, baseDir) {
const normalizedPath = path.normalize(filePath);
const resolvedPath = path.resolve(baseDir || __dirname, normalizedPath);
// __dirname配下にあることを確認
if (!resolvedPath.startsWith(baseDir || __dirname)) {
throw new Error(`Invalid file path: ${filePath}`);
}
// 親ディレクトリへの参照を含まないことを確認
Iif (normalizedPath.includes('..')) {
throw new Error(`Path traversal detected: ${filePath}`);
}
return resolvedPath;
}
function deployTo(label, apiPath, token, modules) {
const body = JSON.stringify({ branch: 'default', modules });
return new Promise((resolve, reject) => {
if (!token) {
console.log(`[${label}] Token not set, skipping.`);
return resolve();
}
const validation = validateToken(token, label);
if (!validation.valid) {
console.log(`[${label}] ${validation.message}, skipping.`);
return resolve();
}
const options = {
hostname: 'screeps.com',
port: 443,
path: apiPath,
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': Buffer.byteLength(body),
'X-Token': token,
},
// SSL証明書の検証を明示的に有効化
rejectUnauthorized: true,
};
console.log(`[${label}] Deploying...`);
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(`[${label}] Status: ${res.statusCode}`);
try {
const json = JSON.parse(data);
if (json.ok === 1) {
console.log(`[${label}] Deployed successfully!`);
resolve();
} else {
// エラーレスポンスから機密情報を除外してログ出力
const safeJson = JSON.stringify(json).replace(/token/gi, '[REDACTED]');
console.error(`[${label}] Deployment failed:`, safeJson);
reject(new Error(`${label} deployment failed`));
}
} catch (e) {
if (res.statusCode === 200) {
console.log(`[${label}] Deployed successfully!`);
resolve();
} else {
// エラーデータから機密情報を除外
const safeData = data.replace(/token/gi, '[REDACTED]');
console.error(`[${label}] Deployment failed! Raw:`, safeData);
reject(new Error(`${label} deployment failed`));
}
}
});
});
req.on('error', (e) => {
// エラーメッセージから機密情報を除外
const safeMessage = e.message.replace(/token/gi, '[REDACTED]');
console.error(`[${label}] Request error:`, safeMessage);
reject(new Error(`${label} request failed`));
});
req.setTimeout(30000, () => {
req.destroy();
reject(new Error(`${label} request timeout`));
});
req.write(body);
req.end();
});
}
// スクリプトとして直接実行された場合のみデプロイ処理を実行
Iif (require.main === module) {
// Read all JS files
const files = [
{ name: 'main', file: 'main.js' },
{ name: 'role.harvester', file: 'role.harvester.js' },
{ name: 'role.upgrader', file: 'role.upgrader.js' },
{ name: 'role.builder', file: 'role.builder.js' },
{ name: 'role.repairer', file: 'role.repairer.js' },
{ name: 'role.explorer', file: 'role.explorer.js' },
];
const modules = {};
console.log('Reading module files...');
for (const m of files) {
try {
// ファイルパスの検証
const filePath = validateFilePath(m.file);
modules[m.name] = fs.readFileSync(filePath, 'utf8');
console.log(` [OK] ${m.name} (${m.file})`);
} catch (e) {
// エラーメッセージから機密情報を除外
const safeMessage = e.message.replace(/token/gi, '[REDACTED]');
console.error(` [ERROR] Failed to read ${m.file}: ${safeMessage}`);
process.exit(1);
}
}
(async () => {
try {
await deployTo('PTR', '/ptr/api/user/code', ptrToken, modules);
await deployTo('PROD', '/api/user/code', prodToken, modules);
console.log('All done!');
} catch (error) {
// 最終的なエラーハンドリング(機密情報のフィルタリング付き)
const safeMessage = error.message.replace(/token/gi, '[REDACTED]');
console.error('Deployment process failed:', safeMessage);
process.exit(1);
}
})();
}
module.exports = { validateToken, validateFilePath, deployTo };
|