Loading SOVA...
Loading SOVA...
Signal detectors inspect manifest and lock file content for supply chain attack patterns. Each detector implements ISignalDetector.
cp src/security/signals/_template/TEMPLATE-signals.ts src/security/signals/mylang-signals.ts
import { createSignal } from '../../types.js';
import type { ISignalDetector, SecuritySignal } from '../../types.js';
export class MylangSignalDetector implements ISignalDetector {
readonly language = 'mylang';
detect(filePath: string, fileContent: string): SecuritySignal[] {
const signals: SecuritySignal[] = [];
if (/run_on_install\s*=\s*true/.test(fileContent)) {
signals.push(createSignal(
filePath,
this.language,
'install-script',
'mylang-install-hook',
'Package declares run_on_install — executes code at install time',
'high',
));
}
const tokenMatch = fileContent.match(
/api_key\s*=\s*["']([A-Za-z0-9_\-]{20,})["']/,
);
if (tokenMatch) {
signals.push(createSignal(
filePath,
this.language,
'hardcoded-secret',
'mylang-hardcoded-api-key',
'Hardcoded API key detected in manifest',
'critical',
tokenMatch[0],
));
}
return signals;
}
}Use createSignal() — it handles content truncation automatically.
Pick the most specific SignalCategory that fits. See the Signal Categories page.
Assign severity conservatively. When in doubt, go one level lower.
Keep regex patterns simple and anchored where possible to avoid ReDoS.
import { MylangSignalDetector } from './signals/mylang-signals.js';
// In the detectors array:
new MylangSignalDetector(),tests/fixtures/signals/mylang/ has-install-hook.toml <- should trigger install-script signal clean.toml <- should produce no signals has-hardcoded-key.toml <- should trigger hardcoded-secret signal
import { describe, it, expect } from 'vitest';
import { MylangSignalDetector } from '../../src/security/signals/mylang-signals.js';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
const detector = new MylangSignalDetector();
const fixture = (name: string) =>
readFileSync(join('tests/fixtures/signals/mylang', name), 'utf-8');
describe('MylangSignalDetector', () => {
it('detects install hook', () => {
const signals = detector.detect(
'pkg.toml',
fixture('has-install-hook.toml'),
);
expect(signals.some(s => s.category === 'install-script')).toBe(true);
});
it('returns no signals for clean manifest', () => {
const signals = detector.detect(
'pkg.toml',
fixture('clean.toml'),
);
expect(signals).toHaveLength(0);
});
it('detects hardcoded secret', () => {
const signals = detector.detect(
'pkg.toml',
fixture('has-hardcoded-key.toml'),
);
const secret = signals.find(s => s.category === 'hardcoded-secret');
expect(secret).toBeDefined();
});
});Open a pull request. Include at least one positive fixture (triggers the signal) and one negative fixture (clean file).