2013-04-14 11 views
7

Ci sono diversi modi per accedere al codice sorgente di una libreria da un codice Ruby che richiede/carica quella libreria. Tra questi modi, alcuni leggono direttamente il file della libreria e lo analizzano. Altri accedono al sorgente attraverso alcuni metodi incorporati che forniscono informazioni sull'origine (come l'albero di sintassi astratto). In una situazione in cui non ho accesso alla lettura diretta del contenuto del file (come nei precedenti modi), l'unico modo per accedere alla fonte sarebbe l'accesso ai metodi incorporati che forniscono le informazioni. Ridefinendo questi metodi per fare qualcos'altro, perderò completamente l'accesso al codice sorgente. Quali sono gli insiemi minimi di metodi tali che se li ridefinisco a qualcos'altro, perderò completamente l'accesso al codice sorgente della libreria su un file esterno?Quali metodi consentono di accedere al codice sorgente?


Per riformulare la domanda

Supponiamo:

  • C'è un utente che può scrivere alcun codice Ruby in file di A.
  • C'è una statica rubino file di B scritto da me, che carica il file A e chiama la routine principale definita in A e definisce anche alcune classi/metodi che l'utente può utilizzare in A.
  • L'utente non ha ve + R (lettura) o + w permesso (scrittura) a B.

Quali metodi (Rubino standard) devo ridefinire (annullare) o rimuovere scrivendo così in file di B in modo da rendere impossibile per l'utente di accedere alla fonte scritta nel file B (tramite qualsiasi codice l'utente può scrivere nel file A) quando eseguo il file B?

Ci sono alcune librerie come lo stregone, la leva, che può estrarre il codice sorgente dei metodi a cui ha accesso. Ci devono essere alcuni comandi primitivi all'interno di Ruby che queste librerie fanno affidamento per consentire loro di accedere al codice sorgente. Quali sono i metodi che rendono possibile questo tipo di cose?

Se non si conosce la risposta completa ma si sa come una particolare libreria estrae l'origine di qualche metodo, ciò sarà comunque di aiuto.

+0

penso che si può definire una classe astratta o qualcosa di simile per interfacciare in Java, che non implementa nulla. L'interfaccia accederà pubblicamente dai componenti esterni. Quindi puoi ereditare la classe astratta per implementarla internamente. –

+0

Quando si ridefiniscono questi metodi, si può fare uso del 'Modulo # alias_method' per assicurarsi di avere una" copia "del metodo originale che si sta modificando. http://apidock.com/ruby/Module/alias_method – fmendez

+1

_system_, _backtick_, _fork/exec_, ecc., devono essere disabilitati. –

risposta

6

TL; DR: Rubino-uniche soluzioni possono utilizzare solo source_location, quindi basta ridefinire questo per restituire qualcosa come ['/some/empty/file', 1]. C hack per l'interprete non utilizzare source_location, ma si può prevenire qualsiasi uso delle estensioni C, bloccando/bianco-listing require e gli amici.


Per uno, per essere in grado di eseguire uno script Ruby, devi essere in grado di leggerlo ...

Ma torniamo alla domanda. So che Sourcify non usa alcun metodo mistico oltre a un piccolo metodo su Proc e Method chiamato source_location, che fornisce il nome file e il numero di riga in cui è definito un metodo/proc. So per esperienza che questo approccio è molto fragile, richiede la scrittura di una sorta di parser e solo a volte funziona in situazioni legittime. Quindi, Sourcify è già fuori se si ridefinisce source_location in B per tornare qualcosa come /dev/null, line 0 e lasciate Sourcify un'eccezione non-Ruby-source.

Da Pry's source, sembra che Pry utilizzi lo stesso approccio source_location, quindi due piccioni con una fava.

Ora, non v'è un'altra opzione per tutte queste biblioteche, che è quello di scendere fino a C e incidere l'interprete per registrare il codice sorgente. Questo è quasi impeccabile. Ma possiamo ancora evitare il pericolo in un modo molto semplice. Qualcuno potrebbe includere tutto il codice per l'origine del metodo di Pry in A. Ma non è possibile includere estensioni C/C in linea senza richiedere una libreria C. Quindi, la soluzione è ovvia: Ridefinire require e require_relative e load a uno non funzionare, o per consentire solo determinate librerie. In questo modo, puoi tenere fuori gli hack C.

Su risonanza magnetica, non c'è modo (da codice Ruby) oltre source_location per fare questo. Quindi eccoti!

Modifica: Secondo @banister, da MRI 2.0+ è incorporato un metodo binding_of_caller che potrebbe sostituire la posizione di origine. Nuke anche questo. ;)

Avviso: Ruby non è un buon linguaggio per questo. Se riesci a metaprogrammarli, possono probabilmente metaprogrammarti, a meno che tu non sia coinvolto in un processo diverso.

+0

Grazie. Alla fine ho ottenuto una risposta che è al punto. Annullando 'source_location', pensi di poter evitare anche l'accesso all'Astratto Syntax Tree (AST)? – sawa

+2

@sawa, quello che stai cercando di fare è estremamente strano, ma fare casino con source_location non sarà abbastanza. Una combinazione di binding_of_caller e Binding # eval ("__ FILE__") ecc restituirà le stesse informazioni di source_location. Chiunque abbia abbastanza intelligenza sarà in grado di aggirare qualsiasi blocco messo in atto, davvero, Ruby è * non * la lingua per questo genere di cose, non provare nemmeno :) – horseyguy

+0

@sawa: Da quello che posso dire, tu può accedere solo all'AST tramite un'estensione C, che non può essere caricata senza controllo sull'ambiente Ruby, o usando 'require'. – Linuxios

0

In realtà è molto semplice se si utilizza il 'method_source' gioiello impressionante di John Mair (il creatore di leva): Il metodo deve essere attuata in Ruby (non C), e deve essere caricato da un file (non irb).

Ecco un esempio che visualizza il codice sorgente metodo nella console Rails con method_source:

$ rails console 
    > require 'method_source' 

    # the following prints out the method code for #lookup in the Rails I18n Backend: 

    > I18n::Backend::Simple.instance_method(:lookup).source.display 
    def lookup(locale, key, scope = [], options = {}) 
     init_translations unless initialized? 
     keys = I18n.normalize_keys(locale, key, scope, options[:separator]) 

     keys.inject(translations) do |result, _key| 
     _key = _key.to_sym 
     return nil unless result.is_a?(Hash) && result.has_key?(_key) 
     result = result[_key] 
     result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol) 
     result 
     end 
    end 
    => nil 

Il codice Ruby che viene visualizzato deve provenire da un file che è stato già caricati, (ovviamente si deve essere leggibile), e deve essere un codice nativo di Ruby (questo non funziona con le librerie compilate/collegate).

Consulta anche:

Problemi correlati