mirror of
https://github.com/mistydemeo/tigerbrew.git
synced 2026-11-08 10:55:21 -05:00
451 lines
15 KiB
Ruby
451 lines
15 KiB
Ruby
class Sphinx < Formula
|
|
desc "Sphinx is a full-text search engine"
|
|
homepage "http://www.sphinxsearch.com"
|
|
|
|
stable do
|
|
url "http://sphinxsearch.com/files/sphinx-2.2.11-release.tar.gz"
|
|
sha256 "6662039f093314f896950519fa781bc87610f926f64b3d349229002f06ac41a9"
|
|
end
|
|
|
|
devel do
|
|
url "http://sphinxsearch.com/files/sphinx-2.3.1-beta.tar.gz"
|
|
sha256 "0e5ebee66fe5b83dd8cbdebffd236dcd7cd33a7633c2e30b23330c65c61ee0e3"
|
|
end
|
|
|
|
head "http://sphinxsearch.googlecode.com/svn/trunk/"
|
|
|
|
bottle do
|
|
sha256 "328b52ebb02d0402fb4122a2f788b81db8f723c07a8f132973d201102d78325d" => :tiger_altivec
|
|
end
|
|
|
|
# There is an implementation of strnlen in sphinx.cpp, use it
|
|
# CVE-2019-14511, Listen on localhost only by default https://bugs.debian.org/939762
|
|
# CVE-2020-29050, Fix random file reading by scattered snippets
|
|
patch :p0, :DATA
|
|
|
|
option "with-mysql", "Force compiling against MySQL"
|
|
option "with-postgresql", "Force compiling against PostgreSQL"
|
|
option "with-id64", "Force compiling with 64-bit ID support"
|
|
|
|
deprecated_option "mysql" => "with-mysql"
|
|
deprecated_option "pgsql" => "with-postgresql"
|
|
deprecated_option "id64" => "with-id64"
|
|
|
|
depends_on "re2" => :optional
|
|
depends_on :mysql => :optional
|
|
depends_on :postgresql => :optional
|
|
depends_on "openssl" if build.with?("mysql")
|
|
depends_on "zlib"
|
|
|
|
resource "stemmer" do
|
|
url "https://github.com/snowballstem/snowball.git",
|
|
:revision => "9b58e92c965cd7e3208247ace3cc00d173397f3c"
|
|
end
|
|
|
|
fails_with :llvm do
|
|
build 2334
|
|
cause "ld: rel32 out of range in _GetPrivateProfileString from /usr/lib/libodbc.a(SQLGetPrivateProfileString.o)"
|
|
end
|
|
|
|
fails_with :clang do
|
|
build 421
|
|
cause "sphinxexpr.cpp:1802:11: error: use of undeclared identifier 'ExprEval'"
|
|
end
|
|
|
|
def install
|
|
resource("stemmer").stage do
|
|
# cp -a is an alias for -pPR which is supported on Tiger
|
|
system "sed -i -e 's/cp -a/cp -pPR/g' GNUmakefile"
|
|
system "make", "dist_libstemmer_c", "CFLAGS=-std=gnu99"
|
|
system "tar", "xzf", "dist/libstemmer_c.tgz", "-C", buildpath
|
|
end
|
|
|
|
args = %W[--prefix=#{prefix}
|
|
--disable-dependency-tracking
|
|
--localstatedir=#{var}
|
|
--with-libstemmer]
|
|
|
|
args << "--enable-id64" if build.with? "id64"
|
|
args << "--with-re2" if build.with? "re2"
|
|
|
|
if build.with? "mysql"
|
|
args << "--with-mysql"
|
|
else
|
|
args << "--without-mysql"
|
|
end
|
|
|
|
if build.with? "postgresql"
|
|
args << "--with-pgsql"
|
|
else
|
|
args << "--without-pgsql"
|
|
end
|
|
|
|
system "./configure", *args
|
|
system "make", "install"
|
|
end
|
|
|
|
def caveats; <<-EOS.undent
|
|
This is not sphinx - the Python Documentation Generator.
|
|
To install sphinx-python: use pip or easy_install,
|
|
|
|
Sphinx has been compiled with libstemmer support.
|
|
|
|
Sphinx depends on either MySQL or PostreSQL as a datasource.
|
|
|
|
You can install these with Tigerbrew with:
|
|
brew install mysql
|
|
For MySQL server.
|
|
|
|
brew install mysql-connector-c
|
|
For MySQL client libraries only.
|
|
|
|
brew install postgresql
|
|
For PostgreSQL server.
|
|
|
|
We don't install these for you when you install this formula, as
|
|
we don't know which datasource you intend to use.
|
|
EOS
|
|
end
|
|
end
|
|
__END__
|
|
--- src/searchd.cpp.orig 2024-02-27 16:37:28.000000000 +0000
|
|
+++ src/searchd.cpp 2024-02-27 16:41:58.000000000 +0000
|
|
@@ -15676,6 +15676,10 @@
|
|
tOut.Eof();
|
|
}
|
|
|
|
+// no strnlen on some OSes (Mac OS), link with implementation in sphinx.cpp
|
|
+#if !HAVE_STRNLEN
|
|
+extern size_t strnlen ( const char * s, size_t iMaxLen );
|
|
+#endif
|
|
|
|
void HandleMysqlShowThreads ( SqlRowBuffer_c & tOut, const SqlStmt_t & tStmt )
|
|
{
|
|
--- sphinx-min.conf.in.orig 2016-07-19 11:42:18.000000000 +0100
|
|
+++ sphinx-min.conf.in 2024-02-27 17:10:05.000000000 +0000
|
|
@@ -49,8 +49,8 @@
|
|
|
|
searchd
|
|
{
|
|
- listen = 9312
|
|
- listen = 9306:mysql41
|
|
+ listen = 127.0.0.1:9312
|
|
+ listen = 127.0.0.1:9306:mysql41
|
|
log = @CONFDIR@/log/searchd.log
|
|
query_log = @CONFDIR@/log/query.log
|
|
read_timeout = 5
|
|
--- sphinx.conf.in
|
|
+++ sphinx.conf.in
|
|
@@ -810,8 +810,8 @@ searchd
|
|
# listen = 192.168.0.1:9312
|
|
# listen = 9312
|
|
# listen = /var/run/searchd.sock
|
|
- listen = 9312
|
|
- listen = 9306:mysql41
|
|
+ listen = 127.0.0.1:9312
|
|
+ listen = 127.0.0.1:9306:mysql41
|
|
|
|
# log file, searchd run info is logged here
|
|
# optional, default is 'searchd.log'
|
|
--- doc/sphinx.html
|
|
+++ doc/sphinx.html
|
|
@@ -11652,7 +11652,7 @@ binlog_max_log_size = 16M
|
|
<div class="sect2" title="12.4.28. snippets_file_prefix"><div class="titlepage"><div><div><h3 class="title"><a name="conf-snippets-file-prefix"></a>12.4.28. snippets_file_prefix</h3></div></div></div>
|
|
<p>
|
|
A prefix to prepend to the local file names when generating snippets.
|
|
-Optional, default is empty.
|
|
+Optional, default is current working folder.
|
|
Introduced in version 2.1.1-beta.
|
|
</p><p>
|
|
This prefix can be used in distributed snippets generation along with
|
|
@@ -11663,6 +11663,19 @@ is set to "server1" and the request refers to "file23", <code class="filename">s
|
|
will attempt to open "server1file23" (all of that without quotes). So if you
|
|
need it to be a path, you have to mention the trailing slash.
|
|
</p><p>
|
|
+After constructing final file path, daemon unwinds all relative dirs and
|
|
+compares final result with the value of ``snippet_file_prefix``. If result
|
|
+is not begin with the prefix, such file will be rejected with error message.
|
|
+
|
|
+So, if you set it to '/mnt/data' and somebody calls snippet generation with file
|
|
+'../../../etc/passwd', as the source, it will get error message
|
|
+`File '/mnt/data/../../../etc/passwd' escapes '/mnt/data/' scope`
|
|
+instead of content of the file.
|
|
+
|
|
+Also, with non-set parameter and reading '/etc/passwd' it will actually read
|
|
+/daemon/working/folder/etc/passwd since default for param is exactly daemon's
|
|
+working folder.
|
|
+</p><p>
|
|
Note also that this is a local option, it does not affect the agents anyhow.
|
|
So you can safely set a prefix on a master server. The requests routed to the
|
|
agents will not be affected by the master's setting. They will however be affected
|
|
@@ -11673,6 +11686,10 @@ This might be useful, for instance, when the document storage locations
|
|
</p><h4><a name="idp33320288"></a>Example:</h4><pre class="programlisting">
|
|
snippets_file_prefix = /mnt/common/server1/
|
|
</pre></div>
|
|
+<p><span class="bold"><strong>WARNING:</strong></span>
|
|
+If you still want to access files from the FS root, you have to explicitly set
|
|
+'snippets_file_prefix' to empty value (by 'snippets_file_prefix=' line), or to
|
|
+root (by 'snippets_file_prefix=/').
|
|
<div class="sect2" title="12.4.29. collation_server"><div class="titlepage"><div><div><h3 class="title"><a name="conf-collation-server"></a>12.4.29. collation_server</h3></div></div></div>
|
|
<p>
|
|
Default server collation.
|
|
--- doc/sphinx.txt
|
|
+++ doc/sphinx.txt
|
|
@@ -12832,7 +12832,7 @@ Example:
|
|
-----------------------------
|
|
|
|
A prefix to prepend to the local file names when generating snippets.
|
|
-Optional, default is empty. Introduced in version 2.1.1-beta.
|
|
+Optional, default is current working folder. Introduced in version 2.1.1-beta.
|
|
|
|
This prefix can be used in distributed snippets generation along with
|
|
load_files or load_files_scattered options.
|
|
@@ -12842,6 +12842,19 @@ to "server1" and the request refers to "file23", searchd will attempt to
|
|
open "server1file23" (all of that without quotes). So if you need it to be
|
|
a path, you have to mention the trailing slash.
|
|
|
|
+After constructing final file path, daemon unwinds all relative dirs and
|
|
+compares final result with the value of ``snippet_file_prefix``. If result
|
|
+is not begin with the prefix, such file will be rejected with error message.
|
|
+
|
|
+So, if you set it to '/mnt/data' and somebody calls snippet generation with file
|
|
+'../../../etc/passwd', as the source, it will get error message
|
|
+`File '/mnt/data/../../../etc/passwd' escapes '/mnt/data/' scope`
|
|
+instead of content of the file.
|
|
+
|
|
+Also, with non-set parameter and reading '/etc/passwd' it will actually read
|
|
+/daemon/working/folder/etc/passwd since default for param is exactly daemon's
|
|
+working folder.
|
|
+
|
|
Note also that this is a local option, it does not affect the agents
|
|
anyhow. So you can safely set a prefix on a master server. The requests
|
|
routed to the agents will not be affected by the master's setting. They
|
|
@@ -12855,6 +12868,10 @@ Example:
|
|
|
|
| snippets_file_prefix = /mnt/common/server1/
|
|
|
|
+WARNING: If you still want to access files from the FS root, you have to
|
|
+explicitly set 'snippets_file_prefix' to empty value (by 'snippets_file_prefix='
|
|
+line), or to root (by 'snippets_file_prefix=/').
|
|
+
|
|
12.4.29. collation_server
|
|
-------------------------
|
|
|
|
--- sphinx.conf.in
|
|
+++ sphinx.conf.in
|
|
@@ -604,7 +604,7 @@ index test1
|
|
# snippet document file name prefix
|
|
# preprended to file names when generating snippets using load_files option
|
|
# WARNING, this is a prefix (not a path), trailing slash matters!
|
|
- # optional, default is empty
|
|
+ # optional, default is current working directory of a running process
|
|
#
|
|
# snippets_file_prefix = /mnt/mydocs/server1
|
|
|
|
@@ -1042,7 +1042,7 @@ searchd
|
|
|
|
# a prefix to prepend to the local file names when creating snippets
|
|
# with load_files and/or load_files_scatter options
|
|
- # optional, default is empty
|
|
+ # optional, default is current working directory of a running process
|
|
#
|
|
# snippets_file_prefix = /mnt/common/server1/
|
|
}
|
|
--- src/searchd.cpp
|
|
+++ src/searchd.cpp
|
|
@@ -13330,6 +13330,12 @@ bool MakeSnippets ( CSphString sIndex, CSphVector<ExcerptQuery_t> & dQueries, CS
|
|
struct stat st;
|
|
CSphString sFilename;
|
|
sFilename.SetSprintf ( "%s%s", g_sSnippetsFilePrefix.cstr(), dQueries[i].m_sSource.cstr() );
|
|
+ if ( !TestEscaping ( g_sSnippetsFilePrefix, sFilename ))
|
|
+ {
|
|
+ sError.SetSprintf( "File '%s' escapes '%s' scope",
|
|
+ sFilename.scstr(), g_sSnippetsFilePrefix.scstr());
|
|
+ return false;
|
|
+ }
|
|
if ( ::stat ( sFilename.cstr(), &st )<0 )
|
|
{
|
|
if ( !bScattered )
|
|
@@ -23719,7 +23725,7 @@ int WINAPI ServiceMain ( int argc, char **argv )
|
|
if ( hSearchd.Exists ( "snippets_file_prefix" ) )
|
|
g_sSnippetsFilePrefix = hSearchd["snippets_file_prefix"].cstr();
|
|
else
|
|
- g_sSnippetsFilePrefix = "";
|
|
+ g_sSnippetsFilePrefix.SetSprintf("%s/", sphGetCwd().scstr());
|
|
|
|
const char* sLogFormat = hSearchd.GetStr ( "query_log_format", "plain" );
|
|
if ( !strcmp ( sLogFormat, "sphinxql" ) )
|
|
--- src/sphinx.cpp
|
|
+++ src/sphinx.cpp
|
|
@@ -81,9 +81,11 @@
|
|
#include <io.h> // for open()
|
|
|
|
// workaround Windows quirks
|
|
+ #include <direct.h>
|
|
#define popen _popen
|
|
#define pclose _pclose
|
|
#define snprintf _snprintf
|
|
+ #define getcwd _getcwd
|
|
#define sphSeek _lseeki64
|
|
|
|
#define stat _stat64
|
|
@@ -12420,6 +12422,75 @@ static bool sphTruncate ( int iFD )
|
|
#endif
|
|
}
|
|
|
|
+CSphString sphNormalizePath( const CSphString& sOrigPath )
|
|
+{
|
|
+ CSphVector<CSphString> dChunks;
|
|
+ const char* sBegin = sOrigPath.scstr();
|
|
+ const char* sEnd = sBegin + sOrigPath.Length();
|
|
+ const char* sPath = sBegin;
|
|
+ int iLevel = 0;
|
|
+
|
|
+ while ( sPath<sEnd )
|
|
+ {
|
|
+ const char* sSlash = ( char* ) memchr( sPath, '/', sEnd - sPath );
|
|
+ if ( !sSlash )
|
|
+ sSlash = sEnd;
|
|
+
|
|
+ auto int iChunkLen = sSlash - sPath;
|
|
+
|
|
+ switch ( iChunkLen )
|
|
+ {
|
|
+ case 0: // empty chunk skipped
|
|
+ ++sPath;
|
|
+ continue;
|
|
+ case 1: // simple dot chunk skipped
|
|
+ if ( *sPath=='.' )
|
|
+ {
|
|
+ sPath += 2;
|
|
+ continue;
|
|
+ }
|
|
+ break;
|
|
+ case 2: // double dot abandons chunks, then decrease level
|
|
+ if ( sPath[0]=='.' && sPath[1]=='.' )
|
|
+ {
|
|
+ if ( dChunks.GetLength() <= 0 )
|
|
+ --iLevel;
|
|
+ else
|
|
+ dChunks.Pop();
|
|
+ sPath += 3;
|
|
+ continue;
|
|
+ }
|
|
+ default: break;
|
|
+ }
|
|
+ CSphString temp( "" );
|
|
+ temp.SetBinary( sPath, iChunkLen );
|
|
+ dChunks.Add( temp );
|
|
+ sPath = sSlash + 1;
|
|
+ }
|
|
+
|
|
+ CSphStringBuilder sResult;
|
|
+ if ( *sBegin=='/' )
|
|
+ sResult += "/";
|
|
+ else
|
|
+ while ( iLevel++<0 )
|
|
+ dChunks.Insert(0, "..");
|
|
+
|
|
+ int i;
|
|
+ for ( i=0; i<dChunks.GetLength(); i++ ) {
|
|
+ sResult += dChunks[i].scstr();
|
|
+ if (i<dChunks.GetLength()-1)
|
|
+ sResult += "/";
|
|
+ }
|
|
+
|
|
+ return sResult.cstr();
|
|
+}
|
|
+
|
|
+CSphString sphGetCwd()
|
|
+{
|
|
+ CSphVector<char> sBuf (65536);
|
|
+ return getcwd( sBuf.Begin(), sBuf.GetLength());
|
|
+}
|
|
+
|
|
class DeleteOnFail : public ISphNoncopyable
|
|
{
|
|
public:
|
|
--- src/sphinxexcerpt.cpp
|
|
+++ src/sphinxexcerpt.cpp
|
|
@@ -3817,6 +3817,11 @@ void sphBuildExcerpt ( ExcerptQuery_t & tOptions, const CSphIndex * pIndex, cons
|
|
{
|
|
CSphString sFilename;
|
|
sFilename.SetSprintf ( "%s%s", tOptions.m_sFilePrefix.cstr(), tOptions.m_sSource.cstr() );
|
|
+ if ( !TestEscaping( tOptions.m_sFilePrefix.scstr(), sFilename ))
|
|
+ {
|
|
+ sError.SetSprintf( "File '%s' escapes '%s' scope", sFilename.scstr(), tOptions.m_sFilePrefix.scstr());
|
|
+ return;
|
|
+ }
|
|
if ( tFile.Open ( sFilename.cstr(), SPH_O_READ, sError )<0 )
|
|
return;
|
|
} else if ( tOptions.m_sSource.IsEmpty() )
|
|
@@ -3859,6 +3864,15 @@ void sphBuildExcerpt ( ExcerptQuery_t & tOptions, const CSphIndex * pIndex, cons
|
|
sWarning, sError, pQueryTokenizer, tOptions.m_dRes );
|
|
}
|
|
|
|
+// check whether filepath from sPath does not escape area of sPrefix
|
|
+bool TestEscaping( const CSphString& sPrefix, const CSphString& sPath )
|
|
+{
|
|
+ if ( sPrefix.IsEmpty() || sPrefix==sPath )
|
|
+ return true;
|
|
+ auto CSphString sNormalized = sphNormalizePath( sPath );
|
|
+ return sPrefix==sNormalized.SubString( 0, sPrefix.Length());
|
|
+}
|
|
+
|
|
//
|
|
// $Id$
|
|
//
|
|
--- src/sphinxexcerpt.h
|
|
+++ src/sphinxexcerpt.h
|
|
@@ -81,6 +81,9 @@ struct XQQuery_t;
|
|
void sphBuildExcerpt ( ExcerptQuery_t & tOptions, const CSphIndex * pIndex, const CSphHTMLStripper * pStripper, const XQQuery_t & tExtQuery,
|
|
DWORD eExtQuerySPZ, CSphString & sWarning, CSphString & sError, CSphDict * pDict, ISphTokenizer * pDocTokenizer, ISphTokenizer * pQueryTokenizer );
|
|
|
|
+// helper whether filepath from sPath does not escape area of sPrefix
|
|
+bool TestEscaping( const CSphString& sPrefix, const CSphString& sPath );
|
|
+
|
|
#endif // _sphinxexcerpt_
|
|
|
|
//
|
|
--- src/sphinxstd.h
|
|
+++ src/sphinxstd.h
|
|
@@ -2294,6 +2294,9 @@ int sphOpenFile ( const char * sFile, CSphString & sError );
|
|
/// return size of file descriptor
|
|
int64_t sphGetFileSize ( int iFD, CSphString & sError );
|
|
|
|
+// unwind different tricks like "../../../etc/passwd"
|
|
+CSphString sphNormalizePath ( const CSphString& sOrigPath );
|
|
+CSphString sphGetCwd();
|
|
|
|
/// buffer trait that neither own buffer nor clean-up it on destroy
|
|
template < typename T >
|
|
--- test/test_130/test.xml
|
|
+++ test/test_130/test.xml
|
|
@@ -7,6 +7,7 @@
|
|
searchd
|
|
{
|
|
<searchd_settings/>
|
|
+ snippets_file_prefix=<this_test/>/
|
|
}
|
|
|
|
source test
|
|
@@ -30,15 +31,15 @@ index test
|
|
|
|
$results = array();
|
|
|
|
-$docs = array( 'test_130/load_file.txt' );
|
|
+$docs = array( "load_file.txt" );
|
|
$opts = array( 'load_files'=>true, 'limit'=>0 );
|
|
|
|
$results[] = $client->BuildExcerpts($docs, 'test', 'end point', $opts );
|
|
$results[] = $client->BuildExcerpts($docs, 'test', 'not_found', $opts );
|
|
-$results[] = $client->BuildExcerpts(array( 'test_130/empty.txt' ), 'test', 'end point', $opts );
|
|
+$results[] = $client->BuildExcerpts(array( 'empty.txt' ), 'test', 'end point', $opts );
|
|
$results[] = $client->BuildExcerpts(array( '' ), 'test', 'not_found', $opts );
|
|
$results[] = $client->GetLastError();
|
|
-$results[] = $client->BuildExcerpts ( array ( 'test_130/512k.xml' ), 'test', 'it builds', array ( "limit" => 100, "load_files" => true ) );
|
|
+$results[] = $client->BuildExcerpts ( array ( '512k.xml' ), 'test', 'it builds', array ( "limit" => 100, "load_files" => true ) );
|
|
|
|
]]></custom_test>
|
|
|