// VIETATO - Non sicuro!
Exec('cmd /c ' + UserInput);
Shell('fpc ' + ProjectPath + ' ' + UserArgs);
// CORRETTO - Parametrizzato
Options.Shell := False;
Args := TStringArray.Create;
Args.Add(ProjectPath);
Spawn('fpc', Args, Options);
**Misure:**
* Usare sempre ''shell: false''
* Passare argomenti come array
* Validare i percorsi prima dell'uso
==== CWE-22: Path Traversal ====
**Rischio:** Accesso a file al di fuori della directory consentita.
// VIETATO - Non sicuro!
FileName := BasePath + UserInput;
ReadFile(FileName);
// CORRETTO - Validazione
function IsPathSafe(const ABasePath, AUserInput: string): Boolean;
var
ResolvedPath: string;
begin
// Non permettere ..
if Pos('..', AUserInput) > 0 then
Exit(False);
ResolvedPath := ExpandFileName(Concat(ABasePath, AUserInput));
Result := Pos(ABasePath, ResolvedPath) = 1;
end;
if IsPathSafe(BasePath, UserInput) then
ReadFile(Concat(BasePath, UserInput));
**Misure:**
* Bloccare sequenze ''..''
* Normalizzare e validare i percorsi
* Il percorso base deve essere prefisso del percorso risultante
==== CWE-20: Input Validation ====
**Rischio:** Dati non validi causano malfunzionamenti o attacchi.
// VIETATO - Nessuna validazione
procedure CreateProject(const AName: string);
begin
MakeDir(AName); // E se AName = '../../../etc'?
end;
// CORRETTO - Validazione
function ValidateProjectName(const AName: string; out AError: string): Boolean;
const
ALLOWED_CHARS = ['a'..'z', 'A'..'Z', '0'..'9', '_', '-'];
MAX_LENGTH = 64;
var
I: Integer;
begin
Result := False;
if AName = '' then
begin
AError := rsProjectNameEmpty;
Exit;
end;
if Length(AName) > MAX_LENGTH then
begin
AError := Format(rsProjectNameTooLong, [MAX_LENGTH]);
Exit;
end;
for I := 1 to Length(AName) do
if not (AName[I] in ALLOWED_CHARS) then
begin
AError := Format(rsProjectNameInvalidChar, [AName[I]]);
Exit;
end;
Result := True;
end;
==== CWE-316: Credenziali in chiaro ====
**Rischio:** Credenziali nel codice o nei log.
// VIETATO - Credenziali nel codice
const
API_TOKEN = 'ghp_xxxxxxxxxxxx';
DB_PASSWORD = 'secret123';
// VIETATO - Loggare credenziali
LogDebug('Token: %s', [Token]);
// CORRETTO - Variabili d'ambiente
Token := GetEnvironmentVariable('GITHUB_TOKEN');
if Token = '' then
raise EWvdSConfigError.Create(rsTokenNotConfigured);
// CORRETTO - Logging mascherato
LogDebug('Token configurato: %s', [BoolToStr(Token <> '', True)]);
==== CWE-532: Log Injection ====
**Rischio:** Dati sensibili visibili nei log.
// VIETATO
LogInfo('Login utente: %s con password: %s', [User, Password]);
LogDebug('Risposta API: %s', [FullResponse]); // Puo contenere token!
// CORRETTO
LogInfo('Login utente: %s', [User]); // Nessuna password
LogDebug('Risposta API ricevuta, lunghezza: %d', [Length(Response)]);
==== CWE-79: Cross-Site Scripting (XSS) ====
**Rischio:** Iniezione di codice script nelle WebView.
// VIETATO - HTML non escapato
WebView.Html := '' + UserInput + '';
// CORRETTO - Escaping
function EscapeHtml(const AText: string): string;
begin
Result := AText;
Result := StringReplace(Result, '&', '&', [rfReplaceAll]);
Result := StringReplace(Result, '<', '<', [rfReplaceAll]);
Result := StringReplace(Result, '>', '>', [rfReplaceAll]);
Result := StringReplace(Result, '"', '"', [rfReplaceAll]);
Result := StringReplace(Result, '''', ''', [rfReplaceAll]);
end;
WebView.Html := '' + EscapeHtml(UserInput) + '';
==== CWE-20: Whitelist messaggi WebView ====
**Rischio:** Tipi di messaggio sconosciuti permettono attacchi ai gestori WebView.
// VIETATO - Nessun controllo tipo messaggio
procedure HandleMessage(AMessage: TJSObject);
var
MsgType: string;
begin
MsgType := string(AMessage['type']);
case MsgType of
'browse': HandleBrowse(AMessage);
'save': HandleSave(AMessage);
end;
end;
// CORRETTO - Con whitelist
const
ALLOWED_MESSAGE_TYPES: array[0..6] of string = (
'browse', 'save', 'cancel', 'validatePath',
'autoDetect', 'config', 'pathSelected'
);
function IsAllowedMessageType(const AType: string): Boolean;
var
I: Integer;
begin
Result := False;
for I := Low(ALLOWED_MESSAGE_TYPES) to High(ALLOWED_MESSAGE_TYPES) do
if ALLOWED_MESSAGE_TYPES[I] = AType then
Exit(True);
end;
procedure HandleMessage(AMessage: TJSObject);
var
MsgType: string;
begin
MsgType := string(AMessage['type']);
// Controllo whitelist PRIMA
if not IsAllowedMessageType(MsgType) then
begin
LogWarning(rsUnknownMessageType, [MsgType]);
Exit;
end;
case MsgType of
'browse': HandleBrowse(AMessage);
'save': HandleSave(AMessage);
// ...
end;
end;
**Misure:**
* Definire whitelist con tutti i tipi di messaggio consentiti
* Loggare e rifiutare tipi sconosciuti
* Aggiornare la whitelist per nuove funzionalita
==== Chiamate API Node.js difensive ====
**Rischio:** Le API Node.js possono avere metodi diversi a seconda del contesto.
// VIETATO - Chiamate metodo dirette senza controllo
procedure SetupProcessHandlers;
begin
asm
this.FProcess.stdout.setEncoding('utf8');
this.FProcess.stderr.setEncoding('utf8');
end;
end;
// CORRETTO - Controlli typeof difensivi
procedure SetupProcessHandlers;
begin
asm
if (this.FProcess && this.FProcess.stdout) {
if (typeof this.FProcess.stdout.setEncoding === 'function') {
this.FProcess.stdout.setEncoding('utf8');
}
if (typeof this.FProcess.stdout.on === 'function') {
this.FProcess.stdout.on('data', this.HandleStdout.bind(this));
}
}
if (this.FProcess && this.FProcess.stderr) {
if (typeof this.FProcess.stderr.setEncoding === 'function') {
this.FProcess.stderr.setEncoding('utf8');
}
if (typeof this.FProcess.stderr.on === 'function') {
this.FProcess.stderr.on('data', this.HandleStderr.bind(this));
}
}
end;
end;
**Misure:**
* ''typeof ... === 'function''' prima di ogni chiamata metodo
* Verificare esistenza oggetto (''if (obj && obj.property)'')
* Gestire gracefully API mancanti
===== Gestione errori =====
==== Nessun handler eccezione vuoto ====
// VIETATO
try
DoSomething;
except
// Ingoiare l'errore
end;
// CORRETTO
try
DoSomething;
except
on E: ESpecificError do
begin
LogError(rsSpecificError, [E.Message]);
// Gestione...
end;
on E: Exception do
begin
LogError(rsUnexpectedError, [E.ClassName, E.Message]);
raise; // O gestire appropriatamente
end;
end;
==== Tipi eccezione specifici ====
// CORRETTO - Eccezioni specifiche
try
Result := DoOperation;
except
on E: EFileNotFoundException do
HandleFileNotFound(E.FileName);
on E: EAccessDenied do
HandleAccessDenied(E.Path);
on E: ENetworkError do
HandleNetworkError(E.Url);
on E: Exception do
HandleUnexpectedError(E);
end;
===== Debug logging =====
Il debug logging richiede **due condizioni**:
- **Compile-time:** ''{$IFDEF DEBUG}''
- **Runtime:** parametro ''--debug''
{$IFDEF DEBUG}
var
DebugLogFile: TextFile;
DebugEnabled: Boolean;
procedure InitDebugLogging;
begin
DebugEnabled := ParamStr(1) = '--debug';
if DebugEnabled then
begin
AssignFile(DebugLogFile, Format('debug-%s.log',
[FormatDateTime('yymmddhhnnss', Now)]));
Rewrite(DebugLogFile);
end;
end;
procedure LogDebugTrace(const AMessage: string; const AArgs: array of const);
begin
if not DebugEnabled then Exit;
WriteLn(DebugLogFile, Format('[%s] %s',
[FormatDateTime('hh:nn:ss.zzz', Now), Format(AMessage, AArgs)]));
Flush(DebugLogFile);
end;
{$ENDIF}
==== Cosa si puo loggare ====
^ Consentito ^ Vietato ^
| Nomi file, percorsi | Token, API key |
| Nomi azioni | Password |
| ID numerici | ID sessione |
| Messaggi errore | Request/response completi |
| Chiavi configurazione | Valori configurazione (sensibili) |
===== Default sicuri =====
// Valori default sicuri
const
DEFAULT_TIMEOUT = 30000; // 30 secondi
MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
MAX_RECURSION_DEPTH = 100;
type
TSpawnOptions = record
Shell: Boolean; // Default: False (sicuro)
Timeout: Integer; // Default: 30000
WorkingDir: string;
Environment: TStringArray;
end;
function DefaultSpawnOptions: TSpawnOptions;
begin
Result.Shell := False; // IMPORTANTE: Nessuna shell!
Result.Timeout := DEFAULT_TIMEOUT;
Result.WorkingDir := '';
Result.Environment := nil;
end;
===== Checklist code review =====
Prima del merge DEVE essere verificato:
SICUREZZA:
[ ] Nessuna credenziale hardcoded
[ ] Tutti gli input utente validati
[ ] Nessuna shell injection possibile
[ ] Nessun path traversal possibile
[ ] HTML escapato nelle WebView
[ ] Nessun dato sensibile nei log
GESTIONE ERRORI:
[ ] Nessun handler eccezione vuoto
[ ] Eccezioni specifiche gestite
[ ] Errori loggati (senza dati sensibili)
CONFIGURAZIONE:
[ ] Default sicuri usati
[ ] Timeout definiti
[ ] Limiti definiti (dimensione file, ricorsione)
===== Audit sicurezza ====
Verifica regolare con strumenti automatizzati:
==== Controlli sicurezza wvds-lint ====
wvds-lint security --path sources/
Viene verificato:
* Chiamate Exec/Shell
* Operazioni path senza validazione
* Stringhe hardcoded (potenziali credenziali)
* Handler eccezione vuoti
* Chiamate log con parametri sensibili
==== Review manuale ====
Per ogni nuova estensione:
* Verificare tutti gli entry point
* Tracciare tutti gli input esterni
* Verificare comunicazione WebView
===== Risposta agli incidenti =====
In caso di scoperta di una vulnerabilita:
- **Non pubblicare** finche la patch non e pronta
- Issue su GitHub con label "security" (privata)
- Sviluppare e testare la patch
- Rilasciare nuova versione
- Pubblicare advisory
===== Vedi anche =====
* [[.:code-konventionen|Convenzioni del codice]]
* [[.:debugging|Debugging]]
* [[.:testing|Testing]]
* [[https://owasp.org/www-project-top-ten/|OWASP Top 10]]